Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 77 additions & 12 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
### Supported TypeScript Constructs

- **Type Definitions**: Type aliases, interfaces, enums, and function declarations
- **Generic Types**: Generic interfaces and type parameters with proper constraint handling
- **Generic Types**: Generic interfaces and type aliases with type parameters and proper constraint handling
- **Complex Types**: Union and intersection types, nested object structures, template literal types
- **Utility Types**: Built-in support for Pick, Omit, Partial, Required, Record, and other TypeScript utility types
- **Utility Types**: Built-in support for Pick, Omit, Partial, Required, Record, Readonly, and other TypeScript utility types
- **Advanced Features**: Conditional types, mapped types, keyof operators, indexed access types
- **Import Resolution**: Cross-file type dependencies with qualified naming and circular dependency handling

Expand All @@ -39,7 +39,7 @@ The main logic for code generation resides in the <mcfile name="index.ts" path="
The `generateCode` function in <mcfile name="index.ts" path="src/index.ts"></mcfile> orchestrates the entire code generation process:

1. **Input Processing**: Creates a `SourceFile` from input using `createSourceFileFromInput`
2. **Generic Interface Detection**: Checks for generic interfaces to determine required TypeBox imports
2. **Generic Type Detection**: Checks for generic interfaces and type aliases to determine required TypeBox imports (including `TSchema`)
3. **Output File Creation**: Creates a new output file with necessary `@sinclair/typebox` imports using `createOutputFile`
4. **Dependency Traversal**: Uses `DependencyTraversal` to analyze and sort all type dependencies
5. **Code Generation**: Processes sorted nodes using `TypeBoxPrinter` in `printSortedNodes`
Expand Down Expand Up @@ -113,8 +113,8 @@ The <mcfile name="base-parser.ts" path="src/parsers/base-parser.ts"></mcfile> pr

#### Specialized Parsers

1. **InterfaceParser**: <mcfile name="parse-interfaces.ts" path="src/parsers/parse-interfaces.ts"></mcfile> - Handles both regular and generic interfaces
2. **TypeAliasParser**: <mcfile name="parse-type-aliases.ts" path="src/parsers/parse-type-aliases.ts"></mcfile> - Processes type alias declarations
1. **InterfaceParser**: <mcfile name="parse-interfaces.ts" path="src/parsers/parse-interfaces.ts"></mcfile> - Handles both regular and generic interfaces using the unified `GenericTypeUtils` flow for consistency with type aliases
2. **TypeAliasParser**: <mcfile name="parse-type-aliases.ts" path="src/parsers/parse-type-aliases.ts"></mcfile> - Processes both regular and generic type alias declarations using `GenericTypeUtils.createGenericArrowFunction`
3. **EnumParser**: <mcfile name="parse-enums.ts" path="src/parsers/parse-enums.ts"></mcfile> - Handles enum declarations
4. **FunctionParser**: <mcfile name="parse-function-declarations.ts" path="src/parsers/parse-function-declarations.ts"></mcfile> - Processes function declarations
5. **ImportParser**: <mcfile name="parse-imports.ts" path="src/parsers/parse-imports.ts"></mcfile> - Handles import resolution
Expand All @@ -127,20 +127,42 @@ The handler system in <mcfile name="handlers/typebox" path="src/handlers/typebox

1. **Base Handlers**: Foundation classes including <mcfile name="base-type-handler.ts" path="src/handlers/typebox/base-type-handler.ts"></mcfile> and specialized base classes
2. **Collection Handlers**: <mcfile name="array-type-handler.ts" path="src/handlers/typebox/collection/array-type-handler.ts"></mcfile>, <mcfile name="intersection-type-handler.ts" path="src/handlers/typebox/collection/intersection-type-handler.ts"></mcfile>, <mcfile name="tuple-type-handler.ts" path="src/handlers/typebox/collection/tuple-type-handler.ts"></mcfile>, <mcfile name="union-type-handler.ts" path="src/handlers/typebox/collection/union-type-handler.ts"></mcfile>
3. **Object Handlers**: <mcfile name="interface-type-handler.ts" path="src/handlers/typebox/object/interface-type-handler.ts"></mcfile>, <mcfile name="object-type-handler.ts" path="src/handlers/typebox/object/object-type-handler.ts"></mcfile>
3. **Object Handlers**: <mcfile name="interface-type-handler.ts" path="src/handlers/typebox/object/interface-type-handler.ts"></mcfile> (returns raw TypeBox expressions for generic interfaces, allowing parser-level arrow function wrapping), <mcfile name="object-type-handler.ts" path="src/handlers/typebox/object/object-type-handler.ts"></mcfile>
4. **Reference Handlers**: <mcfile name="omit-type-handler.ts" path="src/handlers/typebox/reference/omit-type-handler.ts"></mcfile>, <mcfile name="partial-type-handler.ts" path="src/handlers/typebox/reference/partial-type-handler.ts"></mcfile>, <mcfile name="pick-type-handler.ts" path="src/handlers/typebox/reference/pick-type-handler.ts"></mcfile>, <mcfile name="record-type-handler.ts" path="src/handlers/typebox/reference/record-type-handler.ts"></mcfile>, <mcfile name="required-type-handler.ts" path="src/handlers/typebox/reference/required-type-handler.ts"></mcfile>
5. **Simple Handlers**: <mcfile name="simple-type-handler.ts" path="src/handlers/typebox/simple-type-handler.ts"></mcfile>, <mcfile name="literal-type-handler.ts" path="src/handlers/typebox/literal-type-handler.ts"></mcfile>
6. **Advanced Handlers**: <mcfile name="template-literal-type-handler.ts" path="src/handlers/typebox/template-literal-type-handler.ts"></mcfile>, <mcfile name="type-operator-handler.ts" path="src/handlers/typebox/type-operator-handler.ts"></mcfile>, <mcfile name="keyof-type-handler.ts" path="src/handlers/typebox/keyof-type-handler.ts"></mcfile>, <mcfile name="readonly-type-handler.ts" path="src/handlers/typebox/readonly-type-handler.ts"></mcfile>
7. **Function Handlers**: <mcfile name="function-type-handler.ts" path="src/handlers/typebox/function-type-handler.ts"></mcfile>
8. **Type Query Handlers**: <mcfile name="type-query-handler.ts" path="src/handlers/typebox/type-query-handler.ts"></mcfile>, <mcfile name="typeof-type-handler.ts" path="src/handlers/typebox/typeof-type-handler.ts"></mcfile>
9. **Access Handlers**: <mcfile name="indexed-access-type-handler.ts" path="src/handlers/typebox/indexed-access-type-handler.ts"></mcfile>, <mcfile name="type-reference-handler.ts" path="src/handlers/typebox/type-reference-handler.ts"></mcfile>
5. **Readonly Handlers**: <mcfile name="readonly-type-handler.ts" path="src/handlers/typebox/readonly-type-handler.ts"></mcfile>, <mcfile name="readonly-array-type-handler.ts" path="src/handlers/typebox/readonly-array-type-handler.ts"></mcfile>
6. **Simple Handlers**: <mcfile name="simple-type-handler.ts" path="src/handlers/typebox/simple-type-handler.ts"></mcfile>, <mcfile name="literal-type-handler.ts" path="src/handlers/typebox/literal-type-handler.ts"></mcfile>
7. **Advanced Handlers**: <mcfile name="template-literal-type-handler.ts" path="src/handlers/typebox/template-literal-type-handler.ts"></mcfile>, <mcfile name="type-operator-handler.ts" path="src/handlers/typebox/type-operator-handler.ts"></mcfile>, <mcfile name="keyof-type-handler.ts" path="src/handlers/typebox/keyof-type-handler.ts"></mcfile>
8. **Function Handlers**: <mcfile name="function-type-handler.ts" path="src/handlers/typebox/function-type-handler.ts"></mcfile>
9. **Type Query Handlers**: <mcfile name="type-query-handler.ts" path="src/handlers/typebox/type-query-handler.ts"></mcfile>, <mcfile name="typeof-type-handler.ts" path="src/handlers/typebox/typeof-type-handler.ts"></mcfile>
10. **Access Handlers**: <mcfile name="indexed-access-type-handler.ts" path="src/handlers/typebox/indexed-access-type-handler.ts"></mcfile>, <mcfile name="type-reference-handler.ts" path="src/handlers/typebox/type-reference-handler.ts"></mcfile>

#### Readonly Type Handling

The system provides comprehensive support for TypeScript's two distinct readonly constructs through a dual-handler approach:

1. **Readonly Utility Type**: `Readonly<T>` - Handled by <mcfile name="readonly-type-handler.ts" path="src/handlers/typebox/readonly-type-handler.ts"></mcfile>
- Registered as a type reference handler for `Readonly` type references
- Processes `TypeReferenceNode` with identifier "Readonly"
- Generates `Type.Readonly(innerType)` for utility type syntax

2. **Readonly Array Modifier**: `readonly T[]` - Handled by <mcfile name="readonly-array-type-handler.ts" path="src/handlers/typebox/readonly-array-type-handler.ts"></mcfile>
- Extends `TypeOperatorBaseHandler` for `ReadonlyKeyword` operator
- Processes `TypeOperatorTypeNode` with `SyntaxKind.ReadonlyKeyword`
- Generates `Type.Readonly(innerType)` for array modifier syntax
- Registered as a fallback handler to handle complex readonly patterns

This dual approach ensures proper handling of both TypeScript readonly constructs:

- `type ReadonlyUser = Readonly<User>` (utility type)
- `type ReadonlyArray = readonly string[]` (array modifier)
- `type ReadonlyTuple = readonly [string, number]` (tuple modifier)

#### Handler Management

The <mcfile name="typebox-type-handlers.ts" path="src/handlers/typebox/typebox-type-handlers.ts"></mcfile> class orchestrates all handlers through:

- **Handler Caching**: Caches handler instances for performance optimization
- **Fallback System**: Provides fallback handlers for complex cases
- **Fallback System**: Provides fallback handlers for complex cases including readonly array modifiers

### Import Resolution

Expand Down Expand Up @@ -181,6 +203,39 @@ export interface InputOptions {
- **Source Code Input**: Processes TypeScript code directly from strings with validation
- **Project Context**: Enables proper relative import resolution when working with in-memory source files

### Generic Type Support

The codebase provides comprehensive support for both generic interfaces and generic type aliases, enabling complex type transformations and reusable type definitions.

#### Generic Type Aliases

The `TypeAliasParser` handles both regular and generic type aliases through specialized processing:

1. **Type Parameter Detection**: Automatically detects type parameters using `typeAlias.getTypeParameters()`
2. **Function Generation**: Creates TypeBox functions for generic type aliases with proper parameter constraints
3. **TSchema Constraints**: Applies `TSchema` constraints to all type parameters for TypeBox compatibility
4. **Static Type Generation**: Generates corresponding TypeScript type aliases using `Static<ReturnType<typeof TypeName<T>>>`

#### Generic Interface Support

Generic interfaces are processed through the `InterfaceParser` using a consistent architectural pattern that mirrors the type alias flow:

1. **Unified Generic Processing**: The interface parser now uses the same `GenericTypeUtils.createGenericArrowFunction` flow as type aliases for consistency
2. **Raw Expression Handling**: The `InterfaceTypeHandler` returns raw TypeBox expressions for generic interfaces, allowing the parser to handle arrow function wrapping
3. **Parameter Constraint Handling**: Converts TypeScript type parameter constraints to `TSchema` constraints using shared utilities
4. **Function-Based Schema Generation**: Creates TypeBox schema functions that accept type parameters through the standardized generic arrow function pattern
5. **Type Safety Preservation**: Maintains full TypeScript type safety through proper static type aliases using `Static<ReturnType<typeof TypeName<T>>>`
6. **Architectural Consistency**: Both generic interfaces and type aliases now follow the same code generation pattern, improving maintainability and reducing duplication

#### Complex Generic Scenarios

The system supports advanced generic patterns including:

- **Multiple Type Parameters**: Functions with multiple generic parameters (e.g., `ApiResponse<T, E>`)
- **Nested Generic Types**: Generic types that reference other generic types
- **Utility Type Combinations**: Complex combinations like `Partial<Readonly<Record<K, V>>>`
- **Type Parameter Propagation**: Proper handling of type parameters across nested type references

Comment on lines +206 to +238
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Doc reflects invalid TS pattern for generic static aliases. Update guidance.

Static<ReturnType<typeof TypeName<T>>> is not valid TS. Recommend not emitting generic static aliases; advise usage-site inference instead.

-1. **Type Parameter Detection**: Automatically detects type parameters using `typeAlias.getTypeParameters()`
-2. **Function Generation**: Creates TypeBox functions for generic type aliases with proper parameter constraints
-3. **TSchema Constraints**: Applies `TSchema` constraints to all type parameters for TypeBox compatibility
-4. **Static Type Generation**: Generates corresponding TypeScript type aliases using `Static<ReturnType<typeof TypeName<T>>>`
+1. **Type Parameter Detection**: Automatically detects type parameters using `typeAlias.getTypeParameters()`.
+2. **Function Generation**: Creates TypeBox factory functions for generic type aliases with proper parameter constraints.
+3. **TSchema Constraints**: Applies `TSchema` constraints to all type parameters for TypeBox compatibility.
+4. **Static Types (Generics)**: Do not emit generic static aliases due to TypeScript limitations with `typeof` on generic functions. Prefer usage-site inference:
+   `const S = TypeName(Type.String()); type S = Static<typeof S>;`.
-1. **Unified Generic Processing**: The interface parser now uses the same `GenericTypeUtils.createGenericArrowFunction` flow as type aliases for consistency
+1. **Unified Generic Processing**: The interface parser now uses the same `GenericTypeUtils.createGenericArrowFunction` flow as type aliases for consistency.
-5. **Type Safety Preservation**: Maintains full TypeScript type safety through proper static type aliases using `Static<ReturnType<typeof TypeName<T>>>`
+5. **Type Safety Preservation**: Maintains type safety via factory functions; consumers derive statics from concrete instances (`Static<typeof Factory(...)>`).
-6. **Architectural Consistency**: Both generic interfaces and type aliases now follow the same code generation pattern, improving maintainability and reducing duplication
+6. **Architectural Consistency**: Both generic interfaces and type aliases now follow the same code generation pattern, improving maintainability and reducing duplication.
-- **Multiple Type Parameters**: Functions with multiple generic parameters (e.g., `ApiResponse<T, E>`)
-- **Nested Generic Types**: Generic types that reference other generic types
-- **Utility Type Combinations**: Complex combinations like `Partial<Readonly<Record<K, V>>>`
-- **Type Parameter Propagation**: Proper handling of type parameters across nested type references
+- **Multiple Type Parameters**: Functions with multiple generic parameters (e.g., `ApiResponse<T, E>`).
+- **Nested Generic Types**: Generic types that reference other generic types.
+- **Utility Type Combinations**: Complex combinations, like `Partial<Readonly<Record<K, V>>>`.
+- **Type Parameter Propagation**: Proper handling of type parameters across nested type references.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Generic Type Support
The codebase provides comprehensive support for both generic interfaces and generic type aliases, enabling complex type transformations and reusable type definitions.
#### Generic Type Aliases
The `TypeAliasParser` handles both regular and generic type aliases through specialized processing:
1. **Type Parameter Detection**: Automatically detects type parameters using `typeAlias.getTypeParameters()`
2. **Function Generation**: Creates TypeBox functions for generic type aliases with proper parameter constraints
3. **TSchema Constraints**: Applies `TSchema` constraints to all type parameters for TypeBox compatibility
4. **Static Type Generation**: Generates corresponding TypeScript type aliases using `Static<ReturnType<typeof TypeName<T>>>`
#### Generic Interface Support
Generic interfaces are processed through the `InterfaceParser` using a consistent architectural pattern that mirrors the type alias flow:
1. **Unified Generic Processing**: The interface parser now uses the same `GenericTypeUtils.createGenericArrowFunction` flow as type aliases for consistency
2. **Raw Expression Handling**: The `InterfaceTypeHandler` returns raw TypeBox expressions for generic interfaces, allowing the parser to handle arrow function wrapping
3. **Parameter Constraint Handling**: Converts TypeScript type parameter constraints to `TSchema` constraints using shared utilities
4. **Function-Based Schema Generation**: Creates TypeBox schema functions that accept type parameters through the standardized generic arrow function pattern
5. **Type Safety Preservation**: Maintains full TypeScript type safety through proper static type aliases using `Static<ReturnType<typeof TypeName<T>>>`
6. **Architectural Consistency**: Both generic interfaces and type aliases now follow the same code generation pattern, improving maintainability and reducing duplication
#### Complex Generic Scenarios
The system supports advanced generic patterns including:
- **Multiple Type Parameters**: Functions with multiple generic parameters (e.g., `ApiResponse<T, E>`)
- **Nested Generic Types**: Generic types that reference other generic types
- **Utility Type Combinations**: Complex combinations like `Partial<Readonly<Record<K, V>>>`
- **Type Parameter Propagation**: Proper handling of type parameters across nested type references
#### Generic Type Aliases
The `TypeAliasParser` handles both regular and generic type aliases through specialized processing:
1. **Type Parameter Detection**: Automatically detects type parameters using `typeAlias.getTypeParameters()`.
2. **Function Generation**: Creates TypeBox factory functions for generic type aliases with proper parameter constraints.
3. **TSchema Constraints**: Applies `TSchema` constraints to all type parameters for TypeBox compatibility.
4. **Static Types (Generics)**: Do not emit generic static aliases due to TypeScript limitations with `typeof` on generic functions. Prefer usage-site inference:
`const S = TypeName(Type.String()); type S = Static<typeof S>;`.
#### Generic Interface Support
Generic interfaces are processed through the `InterfaceParser` using a consistent architectural pattern that mirrors the type alias flow:
1. **Unified Generic Processing**: The interface parser now uses the same `GenericTypeUtils.createGenericArrowFunction` flow as type aliases for consistency.
2. **Raw Expression Handling**: The `InterfaceTypeHandler` returns raw TypeBox expressions for generic interfaces, allowing the parser to handle arrow function wrapping.
3. **Parameter Constraint Handling**: Converts TypeScript type parameter constraints to `TSchema` constraints using shared utilities.
4. **Function-Based Schema Generation**: Creates TypeBox schema functions that accept type parameters through the standardized generic arrow function pattern.
5. **Type Safety Preservation**: Maintains type safety via factory functions; consumers derive statics from concrete instances (`Static<typeof Factory(...)>`).
6. **Architectural Consistency**: Both generic interfaces and type aliases now follow the same code generation pattern, improving maintainability and reducing duplication.
#### Complex Generic Scenarios
The system supports advanced generic patterns including:
- **Multiple Type Parameters**: Functions with multiple generic parameters (e.g., `ApiResponse<T, E>`).
- **Nested Generic Types**: Generic types that reference other generic types.
- **Utility Type Combinations**: Complex combinations, like `Partial<Readonly<Record<K, V>>>`.
- **Type Parameter Propagation**: Proper handling of type parameters across nested type references.
🧰 Tools
🪛 LanguageTool

[grammar] ~214-~214: There might be a mistake here.
Context: ...matically detects type parameters using typeAlias.getTypeParameters() 2. Function Generation: Creates TypeBox f...

(QB_NEW_EN)


[grammar] ~215-~215: There might be a mistake here.
Context: ...liases with proper parameter constraints 3. TSchema Constraints: Applies TSchema...

(QB_NEW_EN)


[grammar] ~216-~216: There might be a mistake here.
Context: ...ype parameters for TypeBox compatibility 4. Static Type Generation: Generates corr...

(QB_NEW_EN)


[grammar] ~234-~234: There might be a mistake here.
Context: ...c parameters (e.g., ApiResponse<T, E>) - Nested Generic Types: Generic types th...

(QB_NEW_EN)


[grammar] ~235-~235: There might be a mistake here.
Context: ...types that reference other generic types - Utility Type Combinations: Complex com...

(QB_NEW_EN)


[grammar] ~236-~236: There might be a mistake here.
Context: ...mbinations**: Complex combinations like Partial<Readonly<Record<K, V>>> - Type Parameter Propagation: Proper han...

(QB_NEW_EN)

🤖 Prompt for AI Agents
In ARCHITECTURE.md around lines 206-238, the doc incorrectly suggests emitting
generic static aliases using the invalid TypeScript pattern
`Static<ReturnType<typeof TypeName<T>>>`; remove that recommendation and update
the guidance to say we must NOT emit generic static aliases. Instead, explain
that generated schemas for generics should remain as runtime generic factory
functions (no generic `Static` aliases), and recommend consumers rely on
usage-site type inference or create concrete wrapper aliases/instantiations at
call sites (e.g., call the generic schema factory with concrete type parameters
and then apply Static to the resulting non-generic value), and update any
example text to reflect these valid alternatives.

### Interface Inheritance

The codebase provides comprehensive support for TypeScript interface inheritance through a sophisticated dependency resolution and code generation system:
Expand Down Expand Up @@ -234,6 +289,16 @@ The <mcfile name="utils" path="src/utils"></mcfile> directory provides essential
- **TypeBox Expression Generation**: Converts extracted keys into appropriate TypeBox array expressions
- **Shared Utilities**: Provides reusable key extraction logic for Pick, Omit, and other utility type handlers to avoid code duplication

#### Generic Type Utilities

The <mcfile name="generic-type-utils.ts" path="src/utils/generic-type-utils.ts"></mcfile> module provides shared utilities for consistent generic type handling across parsers:

- **Generic Arrow Function Creation**: `createGenericArrowFunction` creates standardized arrow functions for generic types with proper type parameter constraints
- **Type Parameter Processing**: Converts TypeScript type parameters to TypeBox-compatible function parameters with `TSchema` constraints
- **Variable Statement Generation**: `addTypeBoxVariableStatement` creates consistent variable declarations for TypeBox schemas
- **Generic Type Alias Generation**: `addGenericTypeAlias` creates standardized static type aliases using `Static<ReturnType<typeof TypeName<T>>>`
- **Architectural Consistency**: Ensures both interface and type alias parsers follow the same generic type processing pattern

Comment on lines +292 to +301
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Revise utility description: avoid promising generic static alias emission.

Align with the above guidance to prevent generating invalid TS.

-- **Generic Type Alias Generation**: `addGenericTypeAlias` creates standardized static type aliases using `Static<ReturnType<typeof TypeName<T>>>`
+- **Generic Static Alias Policy**: Skip emitting generic static aliases (the `typeof Name<T>` form is not valid TS); rely on usage-site inference (`Static<typeof Factory(...)>`).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#### Generic Type Utilities
The <mcfile name="generic-type-utils.ts" path="src/utils/generic-type-utils.ts"></mcfile> module provides shared utilities for consistent generic type handling across parsers:
- **Generic Arrow Function Creation**: `createGenericArrowFunction` creates standardized arrow functions for generic types with proper type parameter constraints
- **Type Parameter Processing**: Converts TypeScript type parameters to TypeBox-compatible function parameters with `TSchema` constraints
- **Variable Statement Generation**: `addTypeBoxVariableStatement` creates consistent variable declarations for TypeBox schemas
- **Generic Type Alias Generation**: `addGenericTypeAlias` creates standardized static type aliases using `Static<ReturnType<typeof TypeName<T>>>`
- **Architectural Consistency**: Ensures both interface and type alias parsers follow the same generic type processing pattern
#### Generic Type Utilities
The <mcfile name="generic-type-utils.ts" path="src/utils/generic-type-utils.ts"></mcfile> module provides shared utilities for consistent generic type handling across parsers:
- **Generic Arrow Function Creation**: `createGenericArrowFunction` creates standardized arrow functions for generic types with proper type parameter constraints
- **Type Parameter Processing**: Converts TypeScript type parameters to TypeBox-compatible function parameters with `TSchema` constraints
- **Variable Statement Generation**: `addTypeBoxVariableStatement` creates consistent variable declarations for TypeBox schemas
- **Generic Static Alias Policy**: Skip emitting generic static aliases (the `typeof Name<T>` form is not valid TS); rely on usage-site inference (`Static<typeof Factory(...)>`).
- **Architectural Consistency**: Ensures both interface and type alias parsers follow the same generic type processing pattern
🧰 Tools
🪛 LanguageTool

[grammar] ~299-~299: There might be a mistake here.
Context: ... standardized static type aliases using Static<ReturnType<typeof TypeName<T>>> - Architectural Consistency: Ensures bot...

(QB_NEW_EN)

🤖 Prompt for AI Agents
In ARCHITECTURE.md around lines 292 to 301, the Generic Type Utilities section
incorrectly promises that addGenericTypeAlias "creates standardized static type
aliases using Static<ReturnType<typeof TypeName<T>>>", which implies the utility
emits generic static aliases (an invalid/unsupported output). Update the text to
remove the promise of emitting generic static aliases and instead describe the
actual behavior: that the module provides helpers for creating
TypeBox-compatible type parameters, standardized arrow function templates, and
variable statement helpers while explicitly noting that emitting generic
Static<...> aliases is not performed/supported; adjust the bullet for Generic
Type Alias Generation accordingly to state it standardizes type-alias patterns
where valid or documents limitations for generic static aliases.

## Process Overview

1. **Input**: A TypeScript source file containing `enum`, `type alias`, `interface`, and `function` declarations.
Expand Down
79 changes: 18 additions & 61 deletions src/handlers/typebox/object/interface-type-handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ObjectLikeBaseHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/object/object-like-base-handler'
import { makeTypeCall } from '@daxserver/validation-schema-codegen/utils/typebox-codegen-utils'
import { HeritageClause, InterfaceDeclaration, Node, ts, TypeParameterDeclaration } from 'ts-morph'
import { HeritageClause, InterfaceDeclaration, Node, ts } from 'ts-morph'

export class InterfaceTypeHandler extends ObjectLikeBaseHandler {
canHandle(node: Node): boolean {
Expand All @@ -12,11 +12,26 @@ export class InterfaceTypeHandler extends ObjectLikeBaseHandler {
const heritageClauses = node.getHeritageClauses()
const baseObjectType = this.createObjectType(this.processProperties(node.getProperties()))

// If interface has type parameters, generate a function
// For generic interfaces, return raw TypeBox expression
// The parser will handle wrapping it in an arrow function using GenericTypeUtils
if (typeParameters.length > 0) {
return this.createGenericInterfaceFunction(typeParameters, baseObjectType, heritageClauses)
// For generic interfaces, handle inheritance here and return raw expression
if (heritageClauses.length === 0) {
return baseObjectType
}

const extendedTypes = this.collectExtendedTypes(heritageClauses)

if (extendedTypes.length === 0) {
return baseObjectType
}

// Create composite with extended types first, then the current interface
const allTypes = [...extendedTypes, baseObjectType]
return makeTypeCall('Composite', [ts.factory.createArrayLiteralExpression(allTypes, true)])
}

// For non-generic interfaces, handle as before
if (heritageClauses.length === 0) {
return baseObjectType
}
Expand All @@ -33,64 +48,6 @@ export class InterfaceTypeHandler extends ObjectLikeBaseHandler {
return makeTypeCall('Composite', [ts.factory.createArrayLiteralExpression(allTypes, true)])
}

private createGenericInterfaceFunction(
typeParameters: TypeParameterDeclaration[],
baseObjectType: ts.Expression,
heritageClauses: HeritageClause[],
): ts.Expression {
// Create function parameters for each type parameter
const functionParams = typeParameters.map((typeParam) => {
const paramName = typeParam.getName()

return ts.factory.createParameterDeclaration(
undefined,
undefined,
ts.factory.createIdentifier(paramName),
undefined,
ts.factory.createTypeReferenceNode(paramName, undefined),
undefined,
)
})

// Create function body
let functionBody: ts.Expression = baseObjectType

// Handle heritage clauses for generic interfaces
const extendedTypes = this.collectExtendedTypes(heritageClauses)

if (extendedTypes.length > 0) {
const allTypes = [...extendedTypes, baseObjectType]
functionBody = makeTypeCall('Composite', [
ts.factory.createArrayLiteralExpression(allTypes, true),
])
}

// Create type parameters for the function
const functionTypeParams = typeParameters.map((typeParam) => {
const paramName = typeParam.getName()

// Use TSchema as the constraint for TypeBox compatibility
const constraintNode = ts.factory.createTypeReferenceNode('TSchema', undefined)

return ts.factory.createTypeParameterDeclaration(
undefined,
ts.factory.createIdentifier(paramName),
constraintNode,
undefined,
)
})

// Create arrow function
return ts.factory.createArrowFunction(
undefined,
ts.factory.createNodeArray(functionTypeParams),
functionParams,
undefined,
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
functionBody,
)
}

private parseGenericTypeCall(typeText: string): ts.Expression | null {
const match = typeText.match(/^([^<]+)<([^>]+)>$/)

Expand Down
12 changes: 12 additions & 0 deletions src/handlers/typebox/readonly-array-type-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { TypeOperatorBaseHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/type-operator-base-handler'
import { makeTypeCall } from '@daxserver/validation-schema-codegen/utils/typebox-codegen-utils'
import { SyntaxKind, ts } from 'ts-morph'

export class ReadonlyArrayTypeHandler extends TypeOperatorBaseHandler {
protected readonly operatorKind = SyntaxKind.ReadonlyKeyword
protected readonly typeBoxMethod = 'Readonly'

protected createTypeBoxCall(innerType: ts.Expression): ts.Expression {
return makeTypeCall('Readonly', [innerType])
}
}
18 changes: 11 additions & 7 deletions src/handlers/typebox/readonly-type-handler.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { TypeOperatorBaseHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/type-operator-base-handler'
import { TypeReferenceBaseHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/reference/type-reference-base-handler'
import { getTypeBoxType } from '@daxserver/validation-schema-codegen/utils/typebox-call'
import { makeTypeCall } from '@daxserver/validation-schema-codegen/utils/typebox-codegen-utils'
import { SyntaxKind, ts } from 'ts-morph'
import { Node, ts } from 'ts-morph'

export class ReadonlyTypeHandler extends TypeOperatorBaseHandler {
protected readonly operatorKind = SyntaxKind.ReadonlyKeyword
protected readonly typeBoxMethod = 'Readonly'
export class ReadonlyTypeHandler extends TypeReferenceBaseHandler {
protected readonly supportedTypeNames = ['Readonly']
protected readonly expectedArgumentCount = 1

protected createTypeBoxCall(innerType: ts.Expression): ts.Expression {
return makeTypeCall('Readonly', [innerType])
handle(node: Node): ts.Expression {
const typeRef = this.validateTypeReference(node)
const [innerType] = this.extractTypeArguments(typeRef)

return makeTypeCall('Readonly', [getTypeBoxType(innerType)])
}
}
Loading