Skip to content

Commit 975761a

Browse files
authored
feat: Add support for Date type in TypeBox codegen (#16)
This change adds a new `DateTypeHandler` class that can handle the `Date` type in TypeScript. The `DateTypeHandler` is registered in the `TypeBoxTypeHandlers` class, allowing the codegen to generate TypeBox expressions for `Date` types. This enhancement provides native support for the JavaScript `Date` type, making it easier to work with date-related data in the generated validation schemas.
1 parent df05d59 commit 975761a

4 files changed

Lines changed: 145 additions & 2 deletions

File tree

ARCHITECTURE.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- **Complex Types**: Union and intersection types, nested object structures, template literal types
2929
- **Utility Types**: Built-in support for Pick, Omit, Partial, Required, Record, Readonly, and other TypeScript utility types
3030
- **Advanced Features**: Conditional types, mapped types, keyof operators, indexed access types
31+
- **JavaScript Built-in Types**: Native support for Date type using TypeBox's JavaScript type system
3132
- **Import Resolution**: Cross-file type dependencies with qualified naming and circular dependency handling
3233

3334
## Core Components
@@ -133,8 +134,9 @@ The handler system in <mcfile name="handlers/typebox" path="src/handlers/typebox
133134
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>
134135
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>
135136
8. **Function Handlers**: <mcfile name="function-type-handler.ts" path="src/handlers/typebox/function-type-handler.ts"></mcfile>
136-
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>
137-
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>
137+
9. **JavaScript Type Handlers**: <mcfile name="date-type-handler.ts" path="src/handlers/typebox/date-type-handler.ts"></mcfile> - Handles JavaScript built-in types like Date using TypeBox's extended type system
138+
10. **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>
139+
11. **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>
138140

139141
#### Readonly Type Handling
140142

@@ -157,11 +159,20 @@ This dual approach ensures proper handling of both TypeScript readonly construct
157159
- `type ReadonlyArray = readonly string[]` (array modifier)
158160
- `type ReadonlyTuple = readonly [string, number]` (tuple modifier)
159161

162+
#### JavaScript Built-in Type Support
163+
164+
The system provides comprehensive support for JavaScript built-in types through specialized handlers:
165+
166+
- **Date Type Handler**: The <mcfile name="date-type-handler.ts" path="src/handlers/typebox/date-type-handler.ts"></mcfile> handles TypeScript's `Date` type references and converts them to TypeBox's `Type.Date()` schema
167+
- **Type Reference Registration**: JavaScript built-in types are registered in the `typeReferenceHandlers` map for O(1) lookup performance
168+
- **Extended Type System**: Leverages TypeBox's JavaScript type system for types that extend beyond standard JSON Schema
169+
160170
#### Handler Management
161171

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

164174
- **Handler Caching**: Caches handler instances for performance optimization
175+
- **Type Reference Mapping**: O(1) lookup for built-in types like Date, utility types like Partial, and other type references
165176
- **Fallback System**: Provides fallback handlers for complex cases including readonly array modifiers
166177

167178
### Import Resolution
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { BaseTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/base-type-handler'
2+
import { makeTypeCall } from '@daxserver/validation-schema-codegen/utils/typebox-codegen-utils'
3+
import { Node, ts, TypeReferenceNode } from 'ts-morph'
4+
5+
export class DateTypeHandler extends BaseTypeHandler {
6+
canHandle(node: TypeReferenceNode): boolean {
7+
const typeName = node.getTypeName()
8+
9+
return Node.isIdentifier(typeName) && typeName.getText() === 'Date'
10+
}
11+
12+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
13+
handle(_node: TypeReferenceNode): ts.Expression {
14+
return makeTypeCall('Date')
15+
}
16+
}

src/handlers/typebox/typebox-type-handlers.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ArrayTypeHandler } from '@daxserver/validation-schema-codegen/handlers/
33
import { IntersectionTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/collection/intersection-type-handler'
44
import { TupleTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/collection/tuple-type-handler'
55
import { UnionTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/collection/union-type-handler'
6+
import { DateTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/date-type-handler'
67
import { FunctionTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/function-type-handler'
78
import { IndexedAccessTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/indexed-access-type-handler'
89
import { KeyOfTypeHandler } from '@daxserver/validation-schema-codegen/handlers/typebox/keyof-type-handler'
@@ -52,6 +53,7 @@ export class TypeBoxTypeHandlers {
5253
const typeofTypeHandler = new TypeofTypeHandler()
5354
const readonlyTypeHandler = new ReadonlyTypeHandler()
5455
const readonlyArrayTypeHandler = new ReadonlyArrayTypeHandler()
56+
const dateTypeHandler = new DateTypeHandler()
5557

5658
// O(1) lookup by SyntaxKind
5759
this.syntaxKindHandlers.set(SyntaxKind.AnyKeyword, simpleTypeHandler)
@@ -83,6 +85,7 @@ export class TypeBoxTypeHandlers {
8385
this.typeReferenceHandlers.set('Omit', omitTypeHandler)
8486
this.typeReferenceHandlers.set('Required', requiredTypeHandler)
8587
this.typeReferenceHandlers.set('Readonly', readonlyTypeHandler)
88+
this.typeReferenceHandlers.set('Date', dateTypeHandler)
8689

8790
// Fallback handlers for complex cases
8891
this.fallbackHandlers = [
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { createSourceFile, formatWithPrettier, generateFormattedCode } from '@test-fixtures/utils'
2+
import { beforeEach, describe, expect, test } from 'bun:test'
3+
import { Project } from 'ts-morph'
4+
5+
describe('Date types', () => {
6+
let project: Project
7+
8+
beforeEach(() => {
9+
project = new Project()
10+
})
11+
12+
test('without export', () => {
13+
const sourceFile = createSourceFile(project, `type A = Date`)
14+
15+
expect(generateFormattedCode(sourceFile)).toBe(
16+
formatWithPrettier(`
17+
export const A = Type.Date();
18+
19+
export type A = Static<typeof A>;
20+
`),
21+
)
22+
})
23+
24+
test('with export', () => {
25+
const sourceFile = createSourceFile(project, `export type A = Date`)
26+
27+
expect(generateFormattedCode(sourceFile)).toBe(
28+
formatWithPrettier(`
29+
export const A = Type.Date();
30+
31+
export type A = Static<typeof A>;
32+
`),
33+
)
34+
})
35+
36+
test('simple Date type alias', () => {
37+
const sourceFile = createSourceFile(project, `type Timestamp = Date`)
38+
39+
expect(generateFormattedCode(sourceFile)).toBe(
40+
formatWithPrettier(`
41+
export const Timestamp = Type.Date();
42+
43+
export type Timestamp = Static<typeof Timestamp>;
44+
`),
45+
)
46+
})
47+
48+
test('Date in object property', () => {
49+
const sourceFile = createSourceFile(
50+
project,
51+
`
52+
interface User {
53+
name: string;
54+
createdAt: Date;
55+
}
56+
`,
57+
)
58+
59+
expect(generateFormattedCode(sourceFile)).toBe(
60+
formatWithPrettier(`
61+
export const User = Type.Object({
62+
name: Type.String(),
63+
createdAt: Type.Date(),
64+
});
65+
66+
export type User = Static<typeof User>;
67+
`),
68+
)
69+
})
70+
71+
test('Date in union type', () => {
72+
const sourceFile = createSourceFile(project, `type Value = string | Date | number`)
73+
74+
expect(generateFormattedCode(sourceFile)).toBe(
75+
formatWithPrettier(`
76+
export const Value = Type.Union([Type.String(), Type.Date(), Type.Number()]);
77+
78+
export type Value = Static<typeof Value>;
79+
`),
80+
)
81+
})
82+
83+
test('Date in array', () => {
84+
const sourceFile = createSourceFile(project, `type Dates = Date[]`)
85+
86+
expect(generateFormattedCode(sourceFile)).toBe(
87+
formatWithPrettier(`
88+
export const Dates = Type.Array(Type.Date());
89+
90+
export type Dates = Static<typeof Dates>;
91+
`),
92+
)
93+
})
94+
95+
test('Date in function parameter and return type', () => {
96+
const sourceFile = createSourceFile(
97+
project,
98+
`
99+
function formatDate(date: Date): string {
100+
return date.toString();
101+
}
102+
`,
103+
)
104+
105+
expect(generateFormattedCode(sourceFile)).toBe(
106+
formatWithPrettier(`
107+
export const formatDate = Type.Function([Type.Date()], Type.String());
108+
109+
export type formatDate = Static<typeof formatDate>;
110+
`),
111+
)
112+
})
113+
})

0 commit comments

Comments
 (0)