Skip to content

Commit f41dfe3

Browse files
authored
Revision 0.34.24 (#1175)
* Support Generic Parameter Syntax * Documentation * Version * ChangeLog
1 parent 8960391 commit f41dfe3

File tree

11 files changed

+211
-78
lines changed

11 files changed

+211
-78
lines changed

changelog/0.34.0.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
### Revision Updates
66

7-
7+
- [Revision 0.34.24](https://github.com/sinclairzx81/typebox/pull/1175)
8+
- Add Support For Generic Parameter Syntax
89
- [Revision 0.34.23](https://github.com/sinclairzx81/typebox/pull/1174)
910
- Inline Instantiate Type Logic Local to Instantiate Module
1011
- [Revision 0.34.22](https://github.com/sinclairzx81/typebox/pull/1171)

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sinclair/typebox",
3-
"version": "0.34.23",
3+
"version": "0.34.24",
44
"description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
55
"keywords": [
66
"typescript",

readme.md

+29-27
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ License MIT
9898
- [Syntax](#syntax)
9999
- [Create](#syntax-create)
100100
- [Parameters](#syntax-parameters)
101-
- [Options](#syntax-options)
102101
- [Generics](#syntax-generics)
102+
- [Options](#syntax-options)
103103
- [TypeRegistry](#typeregistry)
104104
- [Type](#typeregistry-type)
105105
- [Format](#typeregistry-format)
@@ -1306,9 +1306,9 @@ ValuePointer.Set(A, '/z', 1) // A' = { x: 1, y: 1, z: 1
13061306
13071307
## Syntax Types
13081308
1309-
TypeBox has support for parsing TypeScript syntax at runtime as well as statically in the type system. This feature offers a syntactical frontend to the TypeBox type builder.
1309+
TypeBox includes support for parsing TypeScript annotation syntax into TypeBox schematics.
13101310
1311-
Syntax types are available via optional import.
1311+
This feature is provided via optional import.
13121312
13131313
```typescript
13141314
import { Syntax } from '@sinclair/typebox/syntax'
@@ -1318,20 +1318,25 @@ import { Syntax } from '@sinclair/typebox/syntax'
13181318
13191319
### Create
13201320
1321-
Use the Syntax function to create TypeBox types from TypeScript syntax
1321+
Use the Syntax function to create TypeBox types from TypeScript syntax ([Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199&ts=5.8.0-beta#code/JYWwDg9gTgLgBAbzgZQJ4DsYEMAecC+cAZlBCHAOQACAzsOgMYA2WwUA9DKmAKYBGEHOxoZsOCgChQkWIhTYYwBgWKly1OoxZtO3foMkSGEdDXgAVOAF4Uo3AAoABkhwAuOOgCuIPjygAaOFR3Lx8-AkcASjgY2Jj2djhjUwt3cwB5PgArHgYYAB4ECTiS0rLyisrYhNi3OHMAOW9fAOKq9o7OuBqY4PqmsKg2rpHR+MT8AD4JCS5eeut5LEUGfLmeCCJ6ybHKmvWFmyLdk86euDrQlv9h07uy876rv1v7t-GCIA))
13221322
13231323
```typescript
13241324
const T = Syntax(`{ x: number, y: number }`) // const T: TObject<{
13251325
// x: TNumber,
13261326
// y: TNumber
13271327
// }>
1328+
1329+
type T = Static<typeof T> // type T = {
1330+
// x: number,
1331+
// y: number
1332+
// }
13281333
```
13291334
13301335
<a name="syntax-parameters"></a>
13311336
13321337
### Parameters
13331338
1334-
Syntax types can be parameterized to receive exterior types.
1339+
Syntax types can be parameterized to receive exterior types ([Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199&ts=5.8.0-beta#code/JYWwDg9gTgLgBAbzgZQJ4DsYEMAecC+cAZlBCHAOQACAzsOgMYA2WwUA9DKmAKYBGEHOxoZsOCgCgJDCOhrwAKnAC8KUbgAUAAyQ4AXHHQBXEHx5QANHFQHjp8wS0BKOK7ev27ODLmKDCgHk+ACseBhgAHgQJd1i4+ITEpLdPN304BQA5EzNLGOSCwqK4VNcbDOz7KHzi2rqPL3wAPikfeRQVNUxNJCV8Ky0ABSxYYCwmCIUm52LUtvhkfyDQ8Kia+o2C0rh0wLAYYFlxycrcpot1zav47fK9g6OJrJzzFuv3m8amoA))
13351340
13361341
```typescript
13371342
const T = Syntax(`{ x: number, y: number }`) // const T: TObject<{
@@ -1345,35 +1350,19 @@ const S = Syntax({ T }, `Partial<T>`) // const S: TObject<{
13451350
// }>
13461351
```
13471352
1348-
<a name='syntax-options'></a>
1349-
1350-
### Options
13511353
1352-
Options can be passed via the last parameter
1353-
1354-
```typescript
1355-
const T = Syntax(`number`, { // const T = {
1356-
minimum: 0, // type: 'number',
1357-
maximum: 10 // minimum: 0,
1358-
}) // maximum: 10
1359-
// }
1360-
```
13611354
13621355
<a name="syntax-generics"></a>
13631356
13641357
### Generics
13651358
1366-
Generic types can be created using positional argument types ([Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199&ts=5.8.0-beta#code/JYWwDg9gTgLgBAbzgZQJ4DsYEMAecC+cAZlBCHAOQACAzsOgMYA2WwUA9DKmAKYBGEHOxoZsOCgChQkWIhTYYwBgWKly1OoxZtO3foMkSGEdDXgAVAAxwAvClG4AFBQCCUAOYBXED0wAeSwA+CgBKIxMzOHMARlt7TCdXD29fGD9o4LDjUwsAJji0BJxnNy8ff1zMiXCcuAA1HgYYaAKHYqQrABoo6O7zfPxugAMECTg4HAAuKMtOsbhUaZi58YAvJdyJfCGwmsiAISw6Ggam6BpWosckU+aoAmHR8an6xrv07tm4IJWF6dvoAFur1voFfutXmcoEDvsCwVsdtUuLw4IdjgCoBc7MgFEo-MieBAiKijsATm9zoFxtT2Ow4ASSeiKZi4k9qeyOZyudyeTzadSXkgXiDFrC4BDrIN5ryZbK5ez+eNRULpl9RSCJQ9pfKdbqFXS1tMVWLRV8IbF8Nq9da5fzCEA))
1359+
Syntax types support generic parameters in the following way ([Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199&ts=5.8.0-beta#code/JYWwDg9gTgLgBAbzgZQJ4DsYEMAecC+cAZlBCHAOQACAzsOgMYA2WwUA9DKmAKYBGEHOxoZsOCgChQkWIhTYYwBgWKly1OoxZtO3foMkSGEdDXgA1HgxjQ4AXhSjcACgAGAHgAaAGjgBNXwAtAD45CTg4HAAuOB84cLhUGID4iIAvGMD4-FcASgkjEzM4ACEsOhpLa2gae0dMFyQqmygCX1cEBOi4Zuh3AEZfAAZh4O8EpJ6rFvcRuEG4IbGEjKnqqFnh337lnPyJLl5S8uBK6Zq65AUld0OeCCJjit6oGlCIiPZ2ODun05fag5Oh8QaCweCIZCoV8Pt0kN0FpM5qshm0ElCMZisSCYRFJvCYnNJgsUWjseSKeDcXBVgTFr4kb5Vv0COjKezsTD8EA))
13671360
13681361
```typescript
1369-
const T0 = Syntax('Argument<0>')
1370-
const T1 = Syntax('Argument<1>')
1371-
const T2 = Syntax('Argument<2>')
1372-
1373-
const Vector = Syntax({ T0, T1, T2 }, `{
1374-
x: T0,
1375-
y: T1,
1376-
z: T2
1362+
const Vector = Syntax(`<X, Y, Z> {
1363+
x: X,
1364+
y: Y,
1365+
z: Z
13771366
}`)
13781367

13791368
const BasisVectors = Syntax({ Vector }, `{
@@ -1386,7 +1375,20 @@ type BasisVectors = Static<typeof BasisVectors> // type BasisVectors = {
13861375
// x: { x: 1, y: 0, z: 0 },
13871376
// y: { x: 0, y: 1, z: 0 },
13881377
// z: { x: 0, y: 0, z: 1 }
1389-
// }
1378+
// }
1379+
```
1380+
1381+
<a name='syntax-options'></a>
1382+
1383+
### Options
1384+
1385+
Options can be passed via the last parameter
1386+
1387+
```typescript
1388+
const T = Syntax(`number`, { minimum: 42 }) // const T = {
1389+
// type: 'number',
1390+
// minimum: 42
1391+
// }
13901392
```
13911393
13921394
<a name='typeregistry'></a>

src/syntax/runtime.ts

+46
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,46 @@ const Dereference = (context: t.TProperties, key: string): t.TSchema => {
6666
return key in context ? context[key] : t.Ref(key)
6767
}
6868
// ------------------------------------------------------------------
69+
// GenericArgumentList
70+
// ------------------------------------------------------------------
71+
// prettier-ignore
72+
const GenericArgumentListMapping = (results: unknown[]) => {
73+
return (
74+
results.length === 3 ? [results[0], ...results[2] as unknown[]] :
75+
results.length === 2 ? [results[0]] :
76+
results.length === 1 ? [results[0]] :
77+
[]
78+
)
79+
}
80+
// prettier-ignore
81+
const GenericArgumentList = Runtime.Union([
82+
Runtime.Tuple([Runtime.Ident(), Runtime.Const(Comma), Runtime.Ref('GenericArgumentList')]),
83+
Runtime.Tuple([Runtime.Ident(), Runtime.Const(Comma)]),
84+
Runtime.Tuple([Runtime.Ident()]),
85+
Runtime.Tuple([]),
86+
], (results) => GenericArgumentListMapping(results))
87+
// ------------------------------------------------------------------
88+
// GenericArguments
89+
// ------------------------------------------------------------------
90+
// prettier-ignore
91+
const GenericArgumentsContext = (args: string[]) => {
92+
return args.reduce((result, arg, index) => {
93+
return { ...result, [arg]: t.Argument(index) }
94+
}, {})
95+
}
96+
// prettier-ignore
97+
const GenericArgumentsMapping = (results: unknown[]) => {
98+
return results.length === 3
99+
? GenericArgumentsContext(results[1] as string[])
100+
: {}
101+
}
102+
// prettier-ignore
103+
const GenericArguments = Runtime.Tuple([
104+
Runtime.Const(LAngle),
105+
Runtime.Ref('GenericArgumentList'),
106+
Runtime.Const(RAngle),
107+
], results => GenericArgumentsMapping(results))
108+
// ------------------------------------------------------------------
69109
// GenericReference
70110
// ------------------------------------------------------------------
71111
function GenericReferenceMapping(results: unknown[], context: t.TProperties) {
@@ -657,11 +697,17 @@ const Date = Runtime.Const('Date', Runtime.As(t.Date()))
657697
// Uint8Array
658698
// ------------------------------------------------------------------
659699
const Uint8Array = Runtime.Const('Uint8Array', Runtime.As(t.Uint8Array()))
700+
660701
// ------------------------------------------------------------------
661702
// Module
662703
// ------------------------------------------------------------------
663704
// prettier-ignore
664705
export const Module = new Runtime.Module({
706+
// ----------------------------------------------------------------
707+
// Generic Arguments
708+
// ----------------------------------------------------------------
709+
GenericArgumentList,
710+
GenericArguments,
665711
// ----------------------------------------------------------------
666712
// Type Expressions
667713
// ----------------------------------------------------------------

src/syntax/static.ts

+40
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,46 @@ type Dereference<Context extends t.TProperties, Ref extends string> = (
143143
Ref extends keyof Context ? Context[Ref] : t.TRef<Ref>
144144
)
145145
// ------------------------------------------------------------------
146+
// GenericArgumentList
147+
// ------------------------------------------------------------------
148+
// prettier-ignore
149+
interface GenericArgumentListMapping extends Static.IMapping {
150+
output: (
151+
this['input'] extends [infer Ident extends string, Comma, infer Rest extends unknown[]] ? [Ident, ...Rest] :
152+
this['input'] extends [infer Ident extends string, Comma] ? [Ident] :
153+
this['input'] extends [infer Ident extends string] ? [Ident] :
154+
[]
155+
)
156+
}
157+
// prettier-ignore
158+
type GenericArgumentList = Static.Union<[
159+
Static.Tuple<[Static.Ident, Static.Const<Comma>, GenericArgumentList]>,
160+
Static.Tuple<[Static.Ident, Static.Const<Comma>]>,
161+
Static.Tuple<[Static.Ident]>,
162+
Static.Tuple<[]>,
163+
], GenericArgumentListMapping>
164+
// ------------------------------------------------------------------
165+
// GenericArguments
166+
// ------------------------------------------------------------------
167+
// prettier-ignore
168+
type GenericArgumentsContext<Args extends string[], Result extends t.TProperties = {}> = (
169+
Args extends [...infer Left extends string[], infer Right extends string]
170+
? GenericArgumentsContext<Left, Result & { [_ in Right]: t.TArgument<Left['length']> }>
171+
: t.Evaluate<Result>
172+
)
173+
// prettier-ignore
174+
interface GenericArgumentsMapping extends Static.IMapping {
175+
output: this['input'] extends [LAngle, infer Args extends string[], RAngle]
176+
? GenericArgumentsContext<Args>
177+
: never
178+
}
179+
// prettier-ignore
180+
export type GenericArguments = Static.Tuple<[
181+
Static.Const<LAngle>,
182+
GenericArgumentList,
183+
Static.Const<RAngle>,
184+
], GenericArgumentsMapping>
185+
// ------------------------------------------------------------------
146186
// GenericReference
147187
// ------------------------------------------------------------------
148188
// prettier-ignore

src/syntax/syntax.ts

+44-36
Original file line numberDiff line numberDiff line change
@@ -26,80 +26,88 @@ THE SOFTWARE.
2626
2727
---------------------------------------------------------------------------*/
2828

29-
import * as Types from '../type/index'
29+
import * as t from '../type/index'
3030
import { Static } from '../parser/index'
3131
import { Module } from './runtime'
32-
import { Type } from './static'
32+
import { Type, GenericArguments } from './static'
3333

34+
// ------------------------------------------------------------------
35+
// ParseSyntax: Two-Phase Parse
36+
// ------------------------------------------------------------------
37+
// prettier-ignore
38+
type TParseSyntax<Context extends Record<PropertyKey, t.TSchema>, Code extends string> = (
39+
Static.Parse<GenericArguments, Code, {}> extends [infer Args extends t.TProperties, infer Rest extends string]
40+
? Static.Parse<Type, Rest, Context & Args>
41+
: Static.Parse<Type, Code, Context>
42+
)
43+
// prettier-ignore
44+
function ParseSyntax<Context extends Record<PropertyKey, t.TSchema>, Code extends string>(context: Context, code: Code): [] | [t.TSchema, string] {
45+
const results = Module.Parse('GenericArguments', code, {}) // [ArgumentContext, Rest]
46+
return (
47+
results.length === 2
48+
? Module.Parse('Type', results[1], { ...context, ...results[0] })
49+
: Module.Parse('Type', code, context)
50+
) as never
51+
}
3452
// ------------------------------------------------------------------
3553
// NoInfer
3654
// ------------------------------------------------------------------
37-
/** Parses a TSchema type from TypeScript syntax but does not infer schematics */
38-
export function NoInfer<Context extends Record<PropertyKey, Types.TSchema>, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined
39-
/** Parses a TSchema type from TypeScript syntax but does not infer schematics */
40-
export function NoInfer<Code extends string>(code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined
41-
/** Parses a TSchema type from TypeScript syntax but does not infer schematics */
55+
/** Parses a TypeScript annotation into a TypeBox type but does not infer schematics */
56+
export function NoInfer<Context extends Record<PropertyKey, t.TSchema>, Code extends string>(context: Context, code: Code, options?: t.SchemaOptions): t.TSchema
57+
/** Parses a TypeScript annotation into a TypeBox type but does not infer schematics */
58+
export function NoInfer<Code extends string>(code: Code, options?: t.SchemaOptions): t.TSchema
59+
/** Parses a TypeScript annotation into a TypeBox type but does not infer schematics */
4260
// prettier-ignore
43-
export function NoInfer(...args: any[]): Types.TSchema | undefined {
61+
export function NoInfer(...args: any[]): t.TSchema {
4462
const withContext = typeof args[0] === 'string' ? false : true
4563
const [context, code, options] = withContext ? [args[0], args[1], args[2] || {}] : [{}, args[0], args[1] || {}]
46-
const type = Module.Parse('Type', code, context)[0]
47-
return Types.KindGuard.IsSchema(type)
48-
? Types.CloneType(type, options)
49-
: Types.Never(options)
64+
const result = ParseSyntax(context, code)[0]
65+
return t.KindGuard.IsSchema(result)
66+
? t.CloneType(result, options)
67+
: t.Never(options)
5068
}
51-
// ------------------------------------------------------------------
52-
// Syntax
53-
// ------------------------------------------------------------------
54-
/** Infers a TSchema type from TypeScript syntax. */
69+
70+
/** Parses a TypeScript annotation into a TypeBox type */
5571
// prettier-ignore
56-
export type TSyntax<Context extends Record<PropertyKey, Types.TSchema>, Code extends string> = (
57-
Static.Parse<Type, Code, Context> extends [infer Type extends Types.TSchema, string]
58-
? Type
59-
: Types.TNever
72+
export type TSyntax<Context extends Record<PropertyKey, t.TSchema>, Code extends string> = (
73+
TParseSyntax<Context, Code> extends [infer Type extends t.TSchema, string] ? Type : t.TNever
6074
)
61-
/** Parses a TSchema type from TypeScript syntax */
62-
export function Syntax<Context extends Record<PropertyKey, Types.TSchema>, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): TSyntax<Context, Code>
63-
/** Parses a TSchema type from TypeScript syntax */
64-
export function Syntax<Code extends string>(code: Code, options?: Types.SchemaOptions): TSyntax<{}, Code>
65-
/** Parses a TSchema type from TypeScript syntax */
75+
/** Parses a TypeScript annotation into a TypeBox type */
76+
export function Syntax<Context extends Record<PropertyKey, t.TSchema>, Annotation extends string>(context: Context, annotation: Annotation, options?: t.SchemaOptions): TSyntax<Context, Annotation>
77+
/** Parses a TypeScript annotation into a TypeBox type */
78+
export function Syntax<Annotation extends string>(annotation: Annotation, options?: t.SchemaOptions): TSyntax<{}, Annotation>
79+
/** Parses a TypeScript annotation into a TypeBox type */
6680
export function Syntax(...args: any[]): never {
6781
return NoInfer.apply(null, args as never) as never
6882
}
6983
// ------------------------------------------------------------------
7084
// Deprecated
7185
// ------------------------------------------------------------------
7286
/**
73-
* Parses a TSchema type from Syntax.
7487
* @deprecated Use Syntax() function
7588
*/
76-
export function Parse<Context extends Record<PropertyKey, Types.TSchema>, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): TSyntax<Context, Code>
89+
export function Parse<Context extends Record<PropertyKey, t.TSchema>, Annotation extends string>(context: Context, annotation: Annotation, options?: t.SchemaOptions): TSyntax<Context, Annotation>
7790
/**
78-
* Parses a TSchema type from Syntax.
7991
* @deprecated Use Syntax() function
8092
*/
81-
export function Parse<Code extends string>(code: Code, options?: Types.SchemaOptions): TSyntax<{}, Code>
93+
export function Parse<Annotation extends string>(annotation: Annotation, options?: t.SchemaOptions): TSyntax<{}, Annotation>
8294
/**
83-
* Parses a TSchema type from Syntax.
8495
* @deprecated Use Syntax() function
8596
*/
8697
export function Parse(...args: any[]): never {
8798
return NoInfer.apply(null, args as never) as never
8899
}
89100
/**
90-
* Parses a TSchema from TypeScript Syntax
91101
* @deprecated Use NoInfer() function
92102
*/
93-
export function ParseOnly<Context extends Record<PropertyKey, Types.TSchema>, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined
103+
export function ParseOnly<Context extends Record<PropertyKey, t.TSchema>, Code extends string>(context: Context, code: Code, options?: t.SchemaOptions): t.TSchema | undefined
94104
/**
95-
* Parses a TSchema from TypeScript Syntax
96105
* @deprecated Use NoInfer() function
97106
*/
98-
export function ParseOnly<Code extends string>(code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined
107+
export function ParseOnly<Code extends string>(code: Code, options?: t.SchemaOptions): t.TSchema | undefined
99108
/**
100-
* Parses a TSchema from TypeScript Syntax
101109
* @deprecated Use NoInfer() function
102110
*/
103-
export function ParseOnly(...args: any[]): Types.TSchema | undefined {
111+
export function ParseOnly(...args: any[]): t.TSchema | undefined {
104112
return NoInfer.apply(null, args as never) as never
105113
}

0 commit comments

Comments
 (0)