Skip to content

Conversation

@DaxServer
Copy link
Owner

The changes in this commit add support for parsing and generating code for generic type aliases in the validation schema codegen tool.

The main changes are:

  1. Modify the index.ts file to check if any interfaces or type aliases have generic type parameters.
  2. Implement the parseGenericTypeAlias method in the TypeAliasParser class to handle the parsing and code generation for generic type aliases.
  3. Add a new method createGenericTypeAliasFunction to generate the TypeBox function definition for the generic type alias.
  4. Implement the addGenericTypeAlias method to add the generic type alias declaration to the output file.

These changes allow the validation schema codegen tool to properly handle and generate code for generic type aliases, which is an important feature for users who need to define complex schema types.

The changes in this commit add support for parsing and generating code for generic type aliases in the validation schema codegen tool.

The main changes are:

1. Modify the `index.ts` file to check if any interfaces or type aliases have generic type parameters.
2. Implement the `parseGenericTypeAlias` method in the `TypeAliasParser` class to handle the parsing and code generation for generic type aliases.
3. Add a new method `createGenericTypeAliasFunction` to generate the TypeBox function definition for the generic type alias.
4. Implement the `addGenericTypeAlias` method to add the generic type alias declaration to the output file.

These changes allow the validation schema codegen tool to properly handle and generate code for generic type aliases, which is an important feature for users who need to define complex schema types.
@coderabbitai
Copy link

coderabbitai bot commented Aug 29, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Comprehensive generic support for interfaces and type aliases, including multiple parameters, constraints, and nested scenarios.
    • Readonly handling for both utility types and readonly arrays/tuples.
  • Refactor

    • Unified generic processing across parsing and handling, with improved detection and import generation for generics.
  • Documentation

    • Expanded architecture docs detailing generic type flow, handler behavior, interface inheritance ordering, and readonly support.
  • Tests

    • Added extensive generics and readonly test suites; replaced legacy generic interface tests to ensure parity and runtime binding correctness.

Walkthrough

The PR adds unified generic-type processing via a new GenericTypeUtils, extends generic detection to type aliases, refactors interface and type-alias parsers to emit arrow-function factories plus Static aliases, moves generic function generation out of handlers, introduces readonly handling for utility types and readonly arrays/tuples, updates import logic for TSchema, and adds/updates tests and docs.

Changes

Cohort / File(s) Summary
Docs update
ARCHITECTURE.md
Expands architecture doc with detailed generic type support, readonly handling, dependency-ordered inheritance, GenericTypeUtils description, and updated parser/handler flow.
Entry & printer
src/index.ts, src/printer/typebox-printer.ts
Import generation now detects generics on interfaces and type aliases and conditionally adds TSchema as a type-only import; TypeBoxPrinter now calls typeAliasParser.parse(node) (removed parseWithImportFlag usage).
Generic utilities (new)
src/utils/generic-type-utils.ts
Adds GenericTypeUtils with helpers: addTypeBoxVariableStatement, createFunctionParameters, createFunctionTypeParameters, createGenericArrowFunction, addGenericTypeAlias (generates TSchema-constrained type params, arrow functions, and Static aliases).
Parser refactor — interfaces
src/parsers/parse-interfaces.ts
Replaces local generic-alias construction with GenericTypeUtils; regular interfaces emit TypeBox consts; generic interfaces produce arrow-function factories (via GenericTypeUtils) and corresponding Static type aliases; removed local addGenericTypeAlias.
Parser refactor — type aliases
src/parsers/parse-type-aliases.ts
Splits parsing into regular vs generic paths, uses GenericTypeUtils for consts and generic arrow functions, registers Static aliases, removes parseWithImportFlag and manual variable-statement assembly.
Handler — interface behaviour
src/handlers/typebox/object/interface-type-handler.ts
For generic interfaces, handler now returns raw TypeBox expressions (including Composite of heritage) instead of generating a function; parser wraps expression into arrow-function via GenericTypeUtils.
Readonly handling
src/handlers/typebox/readonly-type-handler.ts, src/handlers/typebox/readonly-array-type-handler.ts, src/handlers/typebox/typebox-type-handlers.ts
Refactors ReadonlyTypeHandler to type-reference-based handler for Readonly<T>; adds ReadonlyArrayTypeHandler for readonly array/tuple modifiers; updates handler registry and fallback behavior to prefer array handler for modifier-based readonly.
Tests — generics & readonly
tests/handlers/typebox/generics.test.ts, tests/handlers/typebox/interface-generics-consistency.test.ts, tests/handlers/typebox/interface-generics-runtime-binding.test.ts, tests/handlers/typebox/readonly.test.ts, tests/handlers/typebox/interfaces.test.ts
Adds comprehensive generics and readonly test suites (new tests validating arrow-function factories, Static aliases, constraints, complex scenarios) and removes legacy generic tests from interfaces.test.ts.
Minor utils tweak
src/utils/key-extraction-utils.ts
Removed explicit trailingComma arg when creating array literal for keys; behavior unchanged.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Developer
  participant Printer as TypeBoxPrinter
  participant Parsers as Interface/TypeAlias Parsers
  participant Utils as GenericTypeUtils
  participant Handlers as TypeBox Handlers

  Dev->>Printer: printNode(node)
  Printer->>Parsers: parse(node)
  alt Generic type alias or interface
    Parsers->>Handlers: request TypeBox expression for body
    Handlers-->>Parsers: TypeBox expression (object/composite)
    Parsers->>Utils: createGenericArrowFunction(typeParams, expr)
    Utils-->>Parsers: ArrowFunction (text)
    Parsers->>Utils: addTypeBoxVariableStatement(name, arrowText)
    Parsers->>Utils: addGenericTypeAlias(name, typeParams, printer)
  else Non-generic
    Parsers->>Handlers: request TypeBox expression
    Handlers-->>Parsers: TypeBox expression
    Parsers->>Utils: addTypeBoxVariableStatement(name, exprText)
  end
Loading
sequenceDiagram
  autonumber
  participant Index as src/index.ts
  participant AST as AST Traversal
  participant Imports as Import Builder

  Index->>AST: scan files for generics (interfaces + type aliases)
  AST-->>Index: hasGenericTypes (bool)
  alt hasGenericTypes == true
    Index->>Imports: add `TSchema` as type-only import
  else
    Note over Index: no TSchema import added
  end
Loading
sequenceDiagram
  autonumber
  participant Handlers as TypeBoxTypeHandlers
  participant Node as TS TypeNode
  participant RRef as ReadonlyTypeHandler
  participant RArr as ReadonlyArrayTypeHandler

  Handlers->>Node: dispatch(node)
  alt Utility type `Readonly<T>`
    Handlers->>RRef: handle(type reference)
    RRef-->>Handlers: Type.Readonly(innerConverted)
  else Modifier `readonly` on arrays/tuples
    Handlers->>RArr: handle(readonly modifier)
    RArr-->>Handlers: Type.Readonly(innerArrayExpr)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7a7063c and 8f39572.

📒 Files selected for processing (2)
  • src/utils/generic-type-utils.ts (1 hunks)
  • src/utils/key-extraction-utils.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • src/utils/key-extraction-utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils/generic-type-utils.ts
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch generic-type-aliases

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ARCHITECTURE.md (1)

26-31: Minor grammar/punctuation fixes in “Supported TypeScript Constructs”.

Add periods for consistency and fix hyphenation.

-- **Generic Types**: Generic interfaces and type aliases with type parameters and 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
+- **Complex Types**: Union and intersection types, nested object structures, and 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, and other TypeScript utility types.
-- **Advanced Features**: Conditional types, mapped types, keyof operators, indexed access types
+- **Advanced Features**: Conditional types, mapped types, keyof operators, and indexed access types.
🧹 Nitpick comments (6)
tests/utils.ts (1)

6-11: Prefer building import specifiers via an array to avoid spacing/comma edge cases.

Construct the import list programmatically and join with ", " to remove manual spacing logic and reduce risk of malformed imports.

-const typeboxImport = (withTSchema: boolean, withReadonly: boolean) => {
-  const readonly = withReadonly ? ' Readonly,' : ''
-  const tschema = withTSchema ? ', type TSchema' : ''
-
-  return `import { Type,${readonly} type Static${tschema} } from "@sinclair/typebox";\n`
-}
+const typeboxImport = (withTSchema: boolean, withReadonly: boolean) => {
+  const specs = ['Type', ...(withReadonly ? ['Readonly'] : []), 'type Static', ...(withTSchema ? ['type TSchema'] : [])]
+  return `import { ${specs.join(', ')} } from "@sinclair/typebox";\n`
+}

Also applies to: 10-10

ARCHITECTURE.md (1)

39-47: Wording nit: “Output File Creation” sentence reads awkwardly.

Tighten phrasing.

-3.  **Output File Creation**: Creates a new output file with necessary `@sinclair/typebox` imports using `createOutputFile`
+3.  **Output File Creation**: Creates a new output file with the required `@sinclair/typebox` imports via `createOutputFile`.
src/utils/generic-type-utils.ts (2)

75-83: Minor: NodeArray creation is unnecessary.

Pass arrays directly; factory will wrap as needed.

-    return ts.factory.createArrowFunction(
-      undefined,
-      ts.factory.createNodeArray(functionTypeParams),
-      functionParams,
+    return ts.factory.createArrowFunction(
+      undefined,
+      functionTypeParams,
+      functionParams,
       undefined,
       ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
       functionBody,
     )

142-150: Type parameter printing via printer is fine; ensure stable whitespace.

No functional issue, but consider passing structures instead of preprinted strings to avoid formatting differences across TS versions.

tests/handlers/typebox/interfaces.test.ts (2)

304-329: Boolean-flag readability in helpers.

formatWithPrettier(..., true, true, true) is hard to parse. Prefer an options bag.

-export const formatWithPrettier = (
-  input: string,
-  addImport: boolean = true,
-  withTSchema: boolean = false,
-  withReadonly: boolean = false,
-): string => {
+export interface FormatOptions { addImport?: boolean; withTSchema?: boolean; withReadonly?: boolean }
+export const formatWithPrettier = (input: string, opts: FormatOptions = {}): string => {
+  const { addImport = true, withTSchema = false, withReadonly = false } = opts
   const code = addImport ? `${typeboxImport(withTSchema, withReadonly)}${input}` : input
   return synchronizedPrettier.format(code, prettierOptions)
 }

And update calls accordingly.


331-363: Confirm generic ReturnType syntax in expectations.

If you adopt (typeof Name)<T> in codegen (see utils comment), align test expectations from ReturnType<typeof Name<T>> to ReturnType<(typeof Name)<T>>.

I can update tests after you decide on the canonical syntax.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6075948 and ee7205b.

📒 Files selected for processing (8)
  • ARCHITECTURE.md (4 hunks)
  • src/index.ts (2 hunks)
  • src/parsers/parse-interfaces.ts (3 hunks)
  • src/parsers/parse-type-aliases.ts (2 hunks)
  • src/printer/typebox-printer.ts (1 hunks)
  • src/utils/generic-type-utils.ts (1 hunks)
  • tests/handlers/typebox/interfaces.test.ts (1 hunks)
  • tests/utils.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
tests/handlers/typebox/interfaces.test.ts (1)
tests/utils.ts (3)
  • createSourceFile (13-15)
  • generateFormattedCode (28-40)
  • formatWithPrettier (17-26)
src/parsers/parse-interfaces.ts (1)
src/utils/generic-type-utils.ts (1)
  • GenericTypeUtils (6-151)
src/parsers/parse-type-aliases.ts (4)
src/utils/generic-type-utils.ts (1)
  • GenericTypeUtils (6-151)
src/utils/add-static-type-alias.ts (1)
  • addStaticTypeAlias (4-22)
src/utils/typebox-call.ts (1)
  • getTypeBoxType (9-21)
src/utils/typebox-codegen-utils.ts (1)
  • makeTypeCall (3-12)
tests/utils.ts (1)
src/index.ts (1)
  • generateCode (81-115)
🪛 LanguageTool
ARCHITECTURE.md

[grammar] ~27-~27: There might be a mistake here.
Context: ...arameters and proper constraint handling - Complex Types: Union and intersection ...

(QB_NEW_EN)


[grammar] ~42-~42: There might be a mistake here.
Context: ...ed TypeBox imports (including TSchema) 3. Output File Creation: Creates a new ou...

(QB_NEW_EN)


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

(QB_NEW_EN)


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

(QB_NEW_EN)


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

(QB_NEW_EN)


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

(QB_NEW_EN)


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

(QB_NEW_EN)


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

(QB_NEW_EN)

🔇 Additional comments (17)
tests/utils.ts (1)

17-24: Confirm callers updated for new withReadonly flag.

Signature changes ripple through tests. Ensure all invocations of formatWithPrettier/generateFormattedCode pass the intended withReadonly value (especially where Readonly appears in expectations).

Also applies to: 39-40

src/utils/generic-type-utils.ts (2)

29-44: Parameter typings for generic factories look good.

The value-parameter equals the type-parameter pattern (e.g., T: T) matches the generated API.


49-63: Constraint normalization to TSchema is correct for TypeBox compatibility.

No issues spotted.

src/printer/typebox-printer.ts (2)

44-45: Switch to unified parse is consistent with parser refactor.

Good simplification.


33-37: No obsolete parseWithImportFlag code
No occurrences of parseWithImportFlag detected in the codebase; nothing to remove.

Likely an incorrect or invalid review comment.

src/index.ts (4)

17-22: LGTM: import scaffold.

Defaulting to non-type import for Type and type-only for everything else is sound.


31-35: LGTM: always include Static as type-only.


36-41: LGTM: gate TSchema on generics.


89-95: LGTM: generic detection broadened to type aliases.

src/parsers/parse-interfaces.ts (3)

3-3: LGTM: centralizing via GenericTypeUtils.


5-5: LGTM: import ts for printer usage.


38-39: LGTM: regular interface path uses shared variable-statement helper.

src/parsers/parse-type-aliases.ts (5)

10-17: LGTM: dedupe by processedTypes.


18-26: LGTM: branch generic vs regular.


28-42: LGTM: regular type alias generation via shared utils.


44-70: LGTM: generic function expression for type aliases.

The shared utility usage is correct and consistent.


71-73: TypeScript 5.9.2 supports typeof Name<T> in type queries; keep GenericTypeUtils.addGenericTypeAlias implementation as-is.

Comment on lines 184 to 207
### 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 similarly through the `InterfaceParser`:

1. **Parameter Constraint Handling**: Converts TypeScript type parameter constraints to `TSchema` constraints
2. **Function-Based Schema Generation**: Creates TypeBox schema functions that accept type parameters
3. **Type Safety Preservation**: Maintains full TypeScript type safety through proper static type aliases

#### Complex Generic Scenarios

The system supports advanced generic patterns including:
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consistency: Section headers and list items need terminal punctuation.

Add periods and ensure parallel phrasing in numbered lists (e.g., start with verbs).

-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 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>>>`.
📝 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 similarly through the `InterfaceParser`:
1. **Parameter Constraint Handling**: Converts TypeScript type parameter constraints to `TSchema` constraints
2. **Function-Based Schema Generation**: Creates TypeBox schema functions that accept type parameters
3. **Type Safety Preservation**: Maintains full TypeScript type safety through proper static type aliases
#### Complex Generic Scenarios
The system supports advanced generic patterns including:
#### 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>>>`.
🧰 Tools
🪛 LanguageTool

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

(QB_NEW_EN)


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

(QB_NEW_EN)


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

(QB_NEW_EN)

🤖 Prompt for AI Agents
In ARCHITECTURE.md around lines 184 to 207, several section headers and
numbered/list items lack terminal punctuation and inconsistent phrasing; update
headers and all list items to end with periods and make the numbered list
entries use parallel verb-led phrasing (e.g., "Detect type parameters.", "Create
TypeBox functions.", "Apply TSchema constraints.", "Generate static type
aliases.") so punctuation and grammatical structure are consistent across the
section.

Comment on lines +116 to +134
const typeReferenceWithArgs = ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier(name),
typeArguments,
)

const typeofExpression = ts.factory.createTypeQueryNode(
typeReferenceWithArgs.typeName,
typeReferenceWithArgs.typeArguments,
)

const returnTypeExpression = ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier('ReturnType'),
[typeofExpression],
)

const staticTypeNode = ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier('Static'),
[returnTypeExpression],
)
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

Potentially invalid TypeScript: typeof ${name}<T> inside ReturnType.

ReturnType<typeof A<T>> may not be accepted by all TS versions (commonly requires ReturnType<(typeof A)<T>>). This impacts generated code usability for consumers.

Option A (AST-lite string assembly, simplest and robust across TS versions):

-    const returnTypeExpression = ts.factory.createTypeReferenceNode(
-      ts.factory.createIdentifier('ReturnType'),
-      [typeofExpression],
-    )
-
-    const staticTypeNode = ts.factory.createTypeReferenceNode(
-      ts.factory.createIdentifier('Static'),
-      [returnTypeExpression],
-    )
-
-    const staticType = printer.printNode(
-      ts.EmitHint.Unspecified,
-      staticTypeNode,
-      newSourceFile.compilerNode,
-    )
+    const typeArgs = typeParamNames.join(', ')
+    const staticType = `Static<ReturnType<(typeof ${name})<${typeArgs}>>>`

Option B (keep AST): construct a parenthesized type around a type query, then a generic instantiation. AST for this is verbose; if you want, I can provide the exact nodes.

Please compile a small generated example with your TS baseline to confirm which form is required. If (typeof X)<T> is necessary, update tests expecting typeof X<T>.


Wrap the typeof expression in parentheses before applying type arguments. TS’s grammar only allows an EntityName after typeof (no type arguments), so ReturnType<typeof A<T>> is invalid—use ReturnType<(typeof A)<T>>. Update the AST builder to emit (typeof ${name})<…> and adjust any tests that assert the old form. (tc39.es)

🤖 Prompt for AI Agents
In src/utils/generic-type-utils.ts around lines 116 to 134, the code currently
emits ReturnType<typeof A<T>> which is invalid; change the AST construction so
the typeof query is wrapped in a parenthesized type before applying type
arguments (emit ReturnType<(typeof A)<T>>). Concretely, build the TypeQueryNode
for the identifier, then wrap it in a ParenthesizedTypeNode (use
ts.factory.createParenthesizedType or the equivalent for type nodes) and use
that parenthesized node as the target when creating the subsequent
TypeReferenceNode with type arguments; update any tests that assert the old
unparenthesized form.

Comment on lines 304 to 329
test('generics with complexity', () => {
const sourceFile = createSourceFile(
project,
`
export type LanguageCode = string;
type LanguageRecord<V> = Partial<Readonly<Record<LanguageCode, V>>>;
`,
)

expect(generateFormattedCode(sourceFile)).toBe(
formatWithPrettier(
`
export const LanguageCode = Type.String()
export type LanguageCode = Static<typeof LanguageCode>
export const LanguageRecord = <V extends TSchema>(V: V) => Type.Partial(Readonly(Type.Record(LanguageCode, V)))
export type LanguageRecord<V extends TSchema> = Static<ReturnType<typeof LanguageRecord<V>>>
`,
true,
true,
true,
),
)
})
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify that Readonly is a named export in your TypeBox version.

The expectation imports and calls Readonly(...) as a named import. Some TypeBox versions expose this under Type.Readonly. Ensure compatibility or adjust expectations.

Alternatively, confirm via docs for the target version.


🏁 Script executed:

#!/bin/bash
# Inspect installed TypeBox version and surface exports (if repo has lockfile)
rg -nP '"@sinclair/typebox"' -C2 package.json pnpm-lock.yaml package-lock.json yarn.lock || true

Length of output: 260


🌐 Web query:

Does @sinclair/typebox export a named `Readonly` (import { Readonly }) in current releases, or should `Type.Readonly` be used?

💡 Result:

Use Type.Readonly — there is no top-level named export Readonly to import. The API exposes the Readonly modifier via the Type namespace (e.g. Type.Readonly(...)) and the typings show TReadonly/Type.Readonly usage. [1][2][3]

Sources:

  • type definitions showing TReadonly and Type.Readonly usage. [1]
  • npm README showing Type.Readonly is a Type modifier. [2]
  • GitHub discussion confirming Type.Readonly is for TS static inference and not a separate serializable readOnly keyword. [3]

Use Type.Readonly instead of Readonly. TypeBox only exposes the Readonly modifier under the Type namespace, so update the test in tests/handlers/typebox/interfaces.test.ts (around line 312) to call Type.Readonly(...) rather than a top-level Readonly.

🤖 Prompt for AI Agents
In tests/handlers/typebox/interfaces.test.ts around lines 304 to 329, the test
constructs a TypeBox schema using the global Readonly identifier which doesn't
exist; replace the top-level Readonly call with Type.Readonly so the generated
schema uses the TypeBox Readonly modifier (i.e. change Readonly(...) to
Type.Readonly(...)) and update any corresponding expected formatted output in
the assertion to reflect Type.Readonly in that position.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/handlers/typebox/object/interface-type-handler.ts (1)

51-64: Broken handling of extended generic types (multi-arg/nested) and built-ins.

The regex only supports one type arg and emits identifiers like Record(...)/ReadonlyArray(...) instead of Type.*, which will generate invalid code for cases like extends Record<string, number> or extends ReadonlyArray<T>.

Apply this targeted fix to robustly parse and map common built-ins while still supporting user-defined generic factories:

-  private parseGenericTypeCall(typeText: string): ts.Expression | null {
-    const match = typeText.match(/^([^<]+)<([^>]+)>$/)
-
-    if (match && match[1] && match[2]) {
-      const baseName = match[1].trim()
-      const typeArg = match[2].trim()
-
-      return ts.factory.createCallExpression(ts.factory.createIdentifier(baseName), undefined, [
-        this.createTypeExpression(typeArg),
-      ])
-    }
-
-    return null
-  }
+  private parseGenericTypeCall(typeText: string): ts.Expression | null {
+    const match = typeText.match(/^([^<]+)<(.+)>$/)
+    if (!match) return null
+    const baseName = match[1].trim()
+    const argsText = match[2].trim()
+
+    // Split top-level comma-separated args (handles nested generics)
+    const args: string[] = []
+    let depth = 0, start = 0
+    for (let i = 0; i < argsText.length; i++) {
+      const c = argsText[i]
+      if (c === '<') depth++
+      else if (c === '>') depth--
+      else if (c === ',' && depth === 0) {
+        args.push(argsText.slice(start, i).trim())
+        start = i + 1
+      }
+    }
+    args.push(argsText.slice(start).trim())
+
+    const mapArg = (s: string) => this.createTypeExpression(s)
+
+    switch (baseName) {
+      case 'Record':
+        if (args.length === 2) return makeTypeCall('Record', [mapArg(args[0]), mapArg(args[1])])
+        break
+      case 'Readonly':
+        if (args.length === 1) return makeTypeCall('Readonly', [mapArg(args[0])])
+        break
+      case 'ReadonlyArray':
+        if (args.length === 1) return makeTypeCall('Readonly', [makeTypeCall('Array', [mapArg(args[0])])])
+        break
+      case 'Array':
+        if (args.length === 1) return makeTypeCall('Array', [mapArg(args[0])])
+        break
+      case 'Partial':
+      case 'Pick':
+      case 'Omit':
+      case 'Required':
+        return makeTypeCall(baseName, args.map(mapArg))
+    }
+
+    // Fallback: call generated factory functions, e.g., Foo<T>
+    return ts.factory.createCallExpression(
+      ts.factory.createIdentifier(baseName),
+      undefined,
+      args.map((a) => this.createTypeExpression(a)),
+    )
+  }
♻️ Duplicate comments (2)
src/index.ts (1)

11-11: Resolved: removed hasReadonly from createOutputFile signature.

Matches prior feedback; call sites updated.

ARCHITECTURE.md (1)

27-30: Punctuate and parallelize list items.

End bullets with periods and keep phrasing parallel.

-**Generic Types**: Generic interfaces and type aliases with type parameters and proper constraint handling
+**Generic Types**: Generic interfaces and type aliases with type parameters and proper constraint handling.
-**Utility Types**: Built-in support for Pick, Omit, Partial, Required, Record, Readonly, and other TypeScript utility types
+**Utility Types**: Built-in support for Pick, Omit, Partial, Required, Record, Readonly, and other TypeScript utility types.
🧹 Nitpick comments (8)
src/handlers/typebox/readonly-array-type-handler.ts (1)

9-11: Use the configured method name instead of hardcoding 'Readonly'.

Keeps the handler DRY and consistent with TypeOperatorBaseHandler.

-  protected createTypeBoxCall(innerType: ts.Expression): ts.Expression {
-    return makeTypeCall('Readonly', [innerType])
-  }
+  protected createTypeBoxCall(innerType: ts.Expression): ts.Expression {
+    return makeTypeCall(this.typeBoxMethod, [innerType])
+  }
src/handlers/typebox/object/interface-type-handler.ts (1)

81-98: Prefer AST over string parsing for heritage types.

Using typeNode.getText() is brittle for complex/nested generics. Consider switching to ExpressionWithTypeArguments and converting via existing handlers, or at least reading getExpression() and getTypeArguments() to avoid text regex.

Happy to refactor collectExtendedTypes to AST-based processing if you want a follow-up PR.

src/index.ts (1)

16-26: Minor: explicit type on namedImports is unnecessary.

TS can infer { name: string; isTypeOnly: boolean }[]; optional to drop for brevity.

tests/handlers/typebox/readonly.test.ts (1)

119-152: Add coverage for interface inheritance with built-ins.

Please add tests for:

  • interface I extends Record<string, number> {}
  • interface J<T> extends ReadonlyArray<T> {}

These would have caught the parseGenericTypeCall issues.

tests/handlers/typebox/interface-generics-runtime-binding.test.ts (1)

74-117: Intentional failing test will break CI. Mark as todo/skip or implement support before merge.

If this PR is not making it pass, avoid red builds.

Apply this minimal diff to keep the test as a tracked todo without failing:

-  test('should fail with current implementation - demonstrates the issue', () => {
+  test.todo('GenericContainer<T, U> should generate a generic arrow function wrapper');
-    // This test is designed to fail with the current implementation
-    // to show that we need to fix the generic interface handling
-    const sourceFile = createSourceFile(
-      project,
-      `
-        interface GenericContainer<T, U> {
-          first: T;
-          second: U;
-          metadata: {
-            created: string;
-            updated: string;
-          };
-        }
-      `,
-    )
-
-    const result = generateFormattedCode(sourceFile, true)
-    // ...
-    expect(result).toBe(
-      formatWithPrettier(
-        `
-          export const GenericContainer = <T extends TSchema, U extends TSchema>(T: T, U: U) => Type.Object({
-            first: T,
-            second: U,
-            metadata: Type.Object({
-              created: Type.String(),
-              updated: Type.String(),
-            }),
-          });
-
-          export type GenericContainer<T extends TSchema, U extends TSchema> = Static<ReturnType<typeof GenericContainer<T, U>>>;
-        `,
-        true,
-        true,
-      ),
-    )
-  })
+  // (implementation to be added in a follow-up PR)
ARCHITECTURE.md (3)

41-46: Add terminal periods to flow steps.

-2.  **Generic Type Detection**: Checks for generic interfaces and type aliases to determine required TypeBox imports (including `TSchema`)
+2.  **Generic Type Detection**: Checks for generic interfaces and type aliases to determine required TypeBox imports (including `TSchema`).

116-118: Terminate parser bullets with periods.

-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`
+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`.

139-159: Clarify Readonly coverage and keep bullets concise.

Minor wording for precision.

-Registered as a fallback handler to handle complex readonly patterns
+Registered as a fallback handler for complex readonly-array patterns.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ee7205b and 7a7063c.

📒 Files selected for processing (12)
  • ARCHITECTURE.md (6 hunks)
  • src/handlers/typebox/object/interface-type-handler.ts (2 hunks)
  • src/handlers/typebox/readonly-array-type-handler.ts (1 hunks)
  • src/handlers/typebox/readonly-type-handler.ts (1 hunks)
  • src/handlers/typebox/typebox-type-handlers.ts (3 hunks)
  • src/index.ts (2 hunks)
  • src/parsers/parse-interfaces.ts (3 hunks)
  • tests/handlers/typebox/generics.test.ts (1 hunks)
  • tests/handlers/typebox/interface-generics-consistency.test.ts (1 hunks)
  • tests/handlers/typebox/interface-generics-runtime-binding.test.ts (1 hunks)
  • tests/handlers/typebox/interfaces.test.ts (0 hunks)
  • tests/handlers/typebox/readonly.test.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • tests/handlers/typebox/interfaces.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/parsers/parse-interfaces.ts
🧰 Additional context used
🧬 Code graph analysis (8)
tests/handlers/typebox/readonly.test.ts (1)
tests/utils.ts (3)
  • createSourceFile (12-14)
  • generateFormattedCode (26-37)
  • formatWithPrettier (16-24)
src/handlers/typebox/readonly-array-type-handler.ts (1)
src/utils/typebox-codegen-utils.ts (1)
  • makeTypeCall (3-12)
tests/handlers/typebox/interface-generics-consistency.test.ts (2)
tests/utils.ts (3)
  • createSourceFile (12-14)
  • generateFormattedCode (26-37)
  • formatWithPrettier (16-24)
tests/handlers/typebox/interfaces.test.ts (5)
  • sourceFile (235-263)
  • sourceFile (205-233)
  • project (5-305)
  • sourceFile (72-107)
  • sourceFile (43-70)
src/handlers/typebox/typebox-type-handlers.ts (1)
src/handlers/typebox/readonly-array-type-handler.ts (1)
  • ReadonlyArrayTypeHandler (5-12)
tests/handlers/typebox/generics.test.ts (2)
tests/utils.ts (3)
  • createSourceFile (12-14)
  • generateFormattedCode (26-37)
  • formatWithPrettier (16-24)
tests/handlers/typebox/interfaces.test.ts (2)
  • sourceFile (235-263)
  • sourceFile (72-107)
tests/handlers/typebox/interface-generics-runtime-binding.test.ts (1)
tests/utils.ts (3)
  • createSourceFile (12-14)
  • generateFormattedCode (26-37)
  • formatWithPrettier (16-24)
src/handlers/typebox/object/interface-type-handler.ts (1)
src/utils/typebox-codegen-utils.ts (1)
  • makeTypeCall (3-12)
src/handlers/typebox/readonly-type-handler.ts (2)
src/utils/typebox-codegen-utils.ts (1)
  • makeTypeCall (3-12)
src/utils/typebox-call.ts (1)
  • getTypeBoxType (9-21)
🪛 LanguageTool
ARCHITECTURE.md

[grammar] ~27-~27: There might be a mistake here.
Context: ...arameters and proper constraint handling - Complex Types: Union and intersection ...

(QB_NEW_EN)


[grammar] ~28-~28: There might be a mistake here.
Context: ...bject structures, template literal types - Utility Types: Built-in support for Pi...

(QB_NEW_EN)


[grammar] ~29-~29: There might be a mistake here.
Context: ...only, and other TypeScript utility types - Advanced Features: Conditional types, ...

(QB_NEW_EN)


[grammar] ~42-~42: There might be a mistake here.
Context: ...ed TypeBox imports (including TSchema) 3. Output File Creation: Creates a new ou...

(QB_NEW_EN)


[grammar] ~143-~143: There might be a mistake here.
Context: ...lity Type**: Readonly<T> - Handled by - Registered as a type reference handler f...

(QB_NEW_EN)


[grammar] ~151-~151: There might be a mistake here.
Context: ...ly(innerType)` for array modifier syntax - Registered as a fallback handler to hand...

(QB_NEW_EN)


[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)


[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)

🔇 Additional comments (6)
src/handlers/typebox/readonly-array-type-handler.ts (1)

5-11: Confirm operand constraint (arrays/tuples only).

If TypeOperatorBaseHandler doesn’t restrict Readonly to array/tuple TypeOperatorNodes, add a guard to avoid mis-handling other constructs.

src/handlers/typebox/object/interface-type-handler.ts (1)

15-33: Returning raw TypeBox expressions for generics is fine—verify wrapping.

Ensure GenericTypeUtils (parser layer) wraps these with the generic arrow function and Static<ReturnType<...>> consistently.

src/handlers/typebox/typebox-type-handlers.ts (1)

54-55: Good layering for Readonly forms.

  • Readonly mapped via typeReferenceHandlers.
  • readonly operator handled by the new fallback ReadonlyArrayTypeHandler.
    This avoids collisions and preserves fast-path lookups.

Double-check that ReadonlyArrayTypeHandler.canHandle(...) won’t match Readonly<T> (TypeReference), only the readonly TypeOperator applied to arrays/tuples.

Also applies to: 85-85, 92-93

src/index.ts (1)

81-86: Generic detection now includes type aliases—LGTM.

Ensures TSchema import appears when either interfaces or type aliases are generic.

tests/handlers/typebox/readonly.test.ts (1)

46-60: Covers generic Readonly including TSchema import—nice.

This aligns with the new generic codegen path.

src/handlers/typebox/readonly-type-handler.ts (1)

10-15: Good refactor: type-reference based Readonly handler is clear and correct.

Validation + single-argument extraction + Type.Readonly(...) is consistent with the new handler architecture.

Comment on lines +206 to +238
### 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

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.

Comment on lines +292 to +301
#### 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

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.

Comment on lines +24 to +29
export const A = <T extends TSchema>(T: T) => Type.Object({
a: T
});
export type A<T extends TSchema> = Static<ReturnType<typeof A<T>>>;
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Drop all invalid generic Static alias lines across tests.

Each export type X<...> = Static<ReturnType<typeof X<...>>> usage is not valid TS and will break consumers. Keep the factory functions; let users infer statics from concrete instances via Static<typeof X(...)>.

Example fix for the first case:

   export const A = <T extends TSchema>(T: T) => Type.Object({
     a: T
   });
-
-  export type A<T extends TSchema> = Static<ReturnType<typeof A<T>>>;

Please apply the same removal to:

  • Lines 58-65 (B alias),
  • 89-96 (B alias in extended type case),
  • 125-130 (LanguageRecord alias),
  • 159-164 (UserResponse alias).

Run:

Also applies to: 58-65, 89-96, 125-130, 159-164


🏁 Script executed:

#!/usr/bin/env bash
rg -nP "export\s+type\s+\w+<[^>]+>\s*=\s*Static<\s*ReturnType<\s*typeof\s+\w+<" tests -g "!**/node_modules/**"

Length of output: 2064


Drop all invalid generic Static<ReturnType<typeof…>> type aliases in typebox tests
Remove every export type X<…> = Static<ReturnType<typeof X<…>>>; line in these files—this pattern isn’t valid TS and breaks consumers (keep only the factory functions; users can infer statics via Static<typeof X(...)>):

  • tests/handlers/typebox/readonly.test.ts (line 54)
  • tests/handlers/typebox/interface-generics-runtime-binding.test.ts (lines 35, 66, 111)
  • tests/handlers/typebox/interface-generics-consistency.test.ts (lines 46, 90)
  • tests/handlers/typebox/generics.test.ts (lines 28, 58, 64, 95, 129, 159, 163)
🤖 Prompt for AI Agents
In tests/handlers/typebox/generics.test.ts around lines 24-29, the exported
generic type alias "export type A<T extends TSchema> = Static<ReturnType<typeof
A<T>>>;" is invalid TypeScript; remove that export type line (keep the factory
function export const A = <T extends TSchema>(T: T) => Type.Object({ a: T }) );
for any other similar occurrences in this file (lines 28, 58, 64, 95, 129, 159,
163) delete the "export type X<...> = Static<ReturnType<typeof X<...>>>;" lines
and, if any tests referenced those aliases, update them to use Static<typeof
X(...)> or infer the static type where needed.

Comment on lines +38 to +54
// Both should generate the same arrow function pattern
const expectedPattern = formatWithPrettier(
`
export const Container = <T extends TSchema>(T: T) => Type.Object({
value: T,
id: Type.String(),
});
export type Container<T extends TSchema> = Static<ReturnType<typeof Container<T>>>;
`,
true,
true,
)

expect(interfaceResult).toBe(expectedPattern)
expect(typeAliasResult).toBe(expectedPattern)
})
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

Remove invalid generic Static alias from expected pattern.

Same typeof Name<T> issue as above.

Apply:

         export const Container = <T extends TSchema>(T: T) => Type.Object({
           value: T,
           id: Type.String(),
         });
-
-        export type Container<T extends TSchema> = Static<ReturnType<typeof Container<T>>>;
📝 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
// Both should generate the same arrow function pattern
const expectedPattern = formatWithPrettier(
`
export const Container = <T extends TSchema>(T: T) => Type.Object({
value: T,
id: Type.String(),
});
export type Container<T extends TSchema> = Static<ReturnType<typeof Container<T>>>;
`,
true,
true,
)
expect(interfaceResult).toBe(expectedPattern)
expect(typeAliasResult).toBe(expectedPattern)
})
// Both should generate the same arrow function pattern
const expectedPattern = formatWithPrettier(
`
export const Container = <T extends TSchema>(T: T) => Type.Object({
value: T,
id: Type.String(),
});
`,
true,
true,
)
expect(interfaceResult).toBe(expectedPattern)
expect(typeAliasResult).toBe(expectedPattern)
})
🤖 Prompt for AI Agents
In tests/handlers/typebox/interface-generics-consistency.test.ts around lines 38
to 54, the expectedPattern includes an invalid generic usage in the Static alias
(using typeof Container<T>), so update the expected string to remove the generic
from the typeof expression; replace the line that defines the export type to use
Static<ReturnType<typeof Container>> (no <T> after Container) or otherwise
remove the invalid generic alias so the expectedPattern matches the generated
code.

Comment on lines +76 to +95
// Should generate using the same pattern as type aliases
expect(result).toBe(
formatWithPrettier(
`
export const ApiResponse = <T extends TSchema, E extends TSchema>(T: T, E: E) => Type.Object({
data: T,
error: E,
status: Type.Number(),
metadata: Type.Object({
timestamp: Type.String(),
version: Type.Number(),
}),
});
export type ApiResponse<T extends TSchema, E extends TSchema> = Static<ReturnType<typeof ApiResponse<T, E>>>;
`,
true,
true,
),
)
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

Remove invalid generic Static alias for ApiResponse as well.

           export const ApiResponse = <T extends TSchema, E extends TSchema>(T: T, E: E) => Type.Object({
             data: T,
             error: E,
             status: Type.Number(),
             metadata: Type.Object({
               timestamp: Type.String(),
               version: Type.Number(),
             }),
           });
-
-          export type ApiResponse<T extends TSchema, E extends TSchema> = Static<ReturnType<typeof ApiResponse<T, E>>>;
📝 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
// Should generate using the same pattern as type aliases
expect(result).toBe(
formatWithPrettier(
`
export const ApiResponse = <T extends TSchema, E extends TSchema>(T: T, E: E) => Type.Object({
data: T,
error: E,
status: Type.Number(),
metadata: Type.Object({
timestamp: Type.String(),
version: Type.Number(),
}),
});
export type ApiResponse<T extends TSchema, E extends TSchema> = Static<ReturnType<typeof ApiResponse<T, E>>>;
`,
true,
true,
),
)
export const ApiResponse = <T extends TSchema, E extends TSchema>(T: T, E: E) => Type.Object({
data: T,
error: E,
status: Type.Number(),
metadata: Type.Object({
timestamp: Type.String(),
version: Type.Number(),
}),
});
🤖 Prompt for AI Agents
In tests/handlers/typebox/interface-generics-consistency.test.ts around lines 76
to 95, the expected snapshot contains an invalid generic Static alias for
ApiResponse; remove the generic type args from the Static/ReturnType usage.
Update the expected string so the type alias reads "export type ApiResponse<T
extends TSchema, E extends TSchema> = Static<ReturnType<typeof ApiResponse>>;"
(i.e. keep the generic parameters on the ApiResponse type but call
ReturnType<typeof ApiResponse> without <T, E>) and adjust formatting
accordingly.

Comment on lines +25 to +36
// The generated code should be an arrow function that takes type parameters
// and returns the TypeBox expression, not just the raw TypeBox expression
expect(result).toBe(
formatWithPrettier(
`
export const Container = <T extends TSchema>(T: T) => Type.Object({
value: T,
id: Type.String(),
});
export type Container<T extends TSchema> = Static<ReturnType<typeof Container<T>>>;
`,
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Invalid TypeScript in Static alias: ReturnType<typeof Container<T>> is not legal.

typeof Container<T> is not valid TS syntax; you cannot apply type arguments to a value typeof query. This pattern won't type-check in consumer projects even if string-equality tests pass.

Apply this diff to drop the invalid static alias from the expected output:

           export const Container = <T extends TSchema>(T: T) => Type.Object({
             value: T,
             id: Type.String(),
           });
-
-          export type Container<T extends TSchema> = Static<ReturnType<typeof Container<T>>>;

Run this quick scan to locate all occurrences of the invalid pattern across the repo:


🏁 Script executed:

#!/usr/bin/env bash
rg -nP "ReturnType<\s*typeof\s+\w+<" -g "!**/node_modules/**"

Length of output: 2725


Remove invalid generic static aliases in generator, tests, and docs
The pattern Static<ReturnType<typeof X<T>>> isn’t valid TypeScript and appears in:

  • src/utils/generic-type-utils.ts (addGenericTypeAlias)
  • Tests under tests/handlers/typebox/*
  • Documentation in ARCHITECTURE.md

• In generic-type-utils.ts, stop emitting export type Name<T extends TSchema> = Static<ReturnType<typeof Name<T>>>
• Remove corresponding export type … = Static<ReturnType<typeof …<…>>> lines from all tests and update ARCHITECTURE.md accordingly

@DaxServer DaxServer merged commit df05d59 into main Aug 30, 2025
7 checks passed
@DaxServer DaxServer deleted the generic-type-aliases branch August 30, 2025 16:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants