Skip to content

Commit

Permalink
Revision 0.28.4 (#407)
Browse files Browse the repository at this point in the history
  • Loading branch information
sinclairzx81 committed Apr 21, 2023
1 parent 749b453 commit 11b09a1
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 89 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sinclair/typebox",
"version": "0.28.3",
"version": "0.28.4",
"description": "JSONSchema Type Builder with Static Type Resolution for TypeScript",
"keywords": [
"typescript",
Expand Down
10 changes: 5 additions & 5 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -1476,11 +1476,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m
┌──────────────────────┬────────────┬────────────┬─────────────┐
│ (index) │ CompiledMinifiedCompression
├──────────────────────┼────────────┼────────────┼─────────────┤
typebox/compiler'126.7 kb'' 56.6 kb''2.24 x'
typebox/errors'110.4 kb'' 48.8 kb''2.26 x'
typebox/system' 75.9 kb'' 31.1 kb''2.44 x'
typebox/value'176.4 kb'' 76.4 kb''2.31 x'
typebox' 74.8 kb'' 30.7 kb''2.44 x'
typebox/compiler'127.1 kb'' 56.7 kb''2.24 x'
typebox/errors'110.9 kb'' 48.9 kb''2.27 x'
typebox/system' 76.3 kb'' 31.2 kb''2.44 x'
typebox/value'176.8 kb'' 76.5 kb''2.31 x'
typebox' 75.2 kb'' 30.8 kb''2.44 x'
└──────────────────────┴────────────┴────────────┴─────────────┘
```
Expand Down
77 changes: 48 additions & 29 deletions src/typebox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export type TReadonlyOptional<T extends TSchema> = T & { [Modifier]: 'ReadonlyOp
// --------------------------------------------------------------------------
// Key
// --------------------------------------------------------------------------
export type Key = keyof any
export type Key = string | number
// --------------------------------------------------------------------------
// TSchema
// --------------------------------------------------------------------------
Expand Down Expand Up @@ -310,21 +310,20 @@ export interface TFunction<T extends readonly TSchema[] = TSchema[], U extends T
// --------------------------------------------------------------------------
// TIndex
// --------------------------------------------------------------------------
export type IndexKey = keyof any
// prettier-ignore
export type TIndexProperty<T extends TProperties, K extends IndexKey> =
K extends keyof T ? [T[K]] :
export type TIndexProperty<T extends TProperties, K extends Key> =
T[K] extends infer R ? [R] :
[]
// prettier-ignore
export type TIndexTuple<T extends TSchema[], K extends IndexKey> =
export type TIndexTuple<T extends TSchema[], K extends Key> =
K extends keyof T ? [T[K]] :
[]
// prettier-ignore
export type TIndexComposite<T extends TSchema[], K extends IndexKey> =
export type TIndexComposite<T extends TSchema[], K extends Key> =
T extends [infer L, ...infer R] ? [...TIndexKey<AssertType<L>, K>, ...TIndexComposite<AssertRest<R>, K>] :
[]
// prettier-ignore
export type TIndexKey<T extends TSchema, K extends IndexKey> =
export type TIndexKey<T extends TSchema, K extends Key> =
T extends TRecursive<infer S> ? TIndexKey<S, K> :
T extends TIntersect<infer S> ? TIndexComposite<S, K> :
T extends TUnion<infer S> ? TIndexComposite<S, K> :
Expand All @@ -333,11 +332,12 @@ export type TIndexKey<T extends TSchema, K extends IndexKey> =
T extends TArray<infer S> ? S :
[]
// prettier-ignore
export type TIndexKeys<T extends TSchema, K extends IndexKey[]> =
K extends [infer L, ...infer R] ? [...TIndexKey<T, Assert<L, IndexKey>>, ...TIndexKeys<T, Assert<R, IndexKey[]>>] :
export type TIndexKeys<T extends TSchema, K extends Key[]> =
K extends [infer L, ...infer R] ?
[...TIndexKey<T, Assert<L, Key>>, ...TIndexKeys<T, Assert<R, Key[]>>] :
[]
// prettier-ignore
export type TIndex<T extends TSchema, K extends IndexKey[]> =
export type TIndex<T extends TSchema, K extends Key[]> =
TIndexKeys<T, K> extends infer R ?
T extends TRecursive<infer S> ? TIndex<S, K> :
T extends TTuple ? UnionType<AssertRest<R>> :
Expand Down Expand Up @@ -540,7 +540,7 @@ export interface TPromise<T extends TSchema = TSchema> extends TSchema {
// --------------------------------------------------------------------------
export type RecordTemplateLiteralObjectType<K extends TTemplateLiteral, T extends TSchema> = Ensure<TObject<Evaluate<{ [_ in Static<K>]: T }>>>
export type RecordTemplateLiteralType<K extends TTemplateLiteral, T extends TSchema> = IsTemplateLiteralFinite<K> extends true ? RecordTemplateLiteralObjectType<K, T> : TRecord<K, T>
export type RecordUnionLiteralType<K extends TUnion<TLiteral<string | number>[]>, T extends TSchema> = Static<K> extends string ? Ensure<TObject<{ [X in Static<K>]: T }>> : never
export type RecordUnionLiteralType<K extends TUnion, T extends TSchema> = Static<K> extends string ? Ensure<TObject<{ [X in Static<K>]: T }>> : never
export type RecordLiteralType<K extends TLiteral<string | number>, T extends TSchema> = Ensure<TObject<{ [K2 in K['const']]: T }>>
export type RecordNumberType<K extends TInteger | TNumber, T extends TSchema> = Ensure<TRecord<K, T>>
export type RecordStringType<K extends TString, T extends TSchema> = Ensure<TRecord<K, T>>
Expand Down Expand Up @@ -1045,9 +1045,9 @@ export namespace TypeGuard {
export function TLiteralBoolean(schema: unknown): schema is TLiteral<boolean> {
return TKind(schema) && schema[Kind] === 'Literal' && IsOptionalString(schema.$id) && typeof schema.const === 'boolean'
}
/** Returns true if the given schema is TUnion<Literal<string>[]> */
export function TLiteralUnion(schema: unknown): schema is TUnion<TLiteral<string>[]> {
return TUnion(schema) && schema.anyOf.every((schema) => TLiteral(schema))
/** Returns true if the given schema is TUnion<Literal<string | number>[]> */
export function TLiteralUnion(schema: unknown): schema is TUnion<TLiteral[]> {
return TUnion(schema) && schema.anyOf.every((schema) => TLiteralString(schema) || TLiteralNumber(schema))
}
/** Returns true if the given schema is TLiteral */
export function TLiteral(schema: unknown): schema is TLiteral {
Expand Down Expand Up @@ -1924,12 +1924,12 @@ export namespace IndexedAccessor {
return schema.anyOf.reduce((acc, schema) => [...acc, ...Visit(schema, key)], [] as TSchema[])
}
function Object(schema: TObject, key: string): TSchema[] {
const keys = globalThis.Object.getOwnPropertyNames(schema.properties).filter((k) => k === key)
const keys = globalThis.Object.getOwnPropertyNames(schema.properties).filter((key_) => key_ === key)
return keys.map((key) => schema.properties[key])
}
function Tuple(schema: TTuple, key: string): TSchema[] {
const items = schema.items === undefined ? [] : (schema.items as TSchema[])
return items.filter((_, index) => index.toString() === key.toString())
return items.filter((_, index) => index.toString() === key)
}
function Visit(schema: TSchema, key: string): TSchema[] {
if (schema[Kind] === 'Intersect') return Intersect(schema as TIntersect, key)
Expand All @@ -1938,8 +1938,8 @@ export namespace IndexedAccessor {
if (schema[Kind] === 'Tuple') return Tuple(schema as TTuple, key)
return []
}
export function Resolve(schema: TSchema, keys: string[]): TSchema[] {
return keys.reduce((acc, key) => [...acc, ...Visit(schema, key)], [] as TSchema[])
export function Resolve(schema: TSchema, keys: (string | number)[]): TSchema[] {
return keys.reduce((acc, key) => [...acc, ...Visit(schema, key.toString())], [] as TSchema[])
}
}
// --------------------------------------------------------------------------
Expand Down Expand Up @@ -2019,7 +2019,7 @@ export namespace KeyArrayResolver {
/** Resolves an array of string[] keys from the given schema or array type. */
export function Resolve(schema: TSchema | string[]): string[] {
if (globalThis.Array.isArray(schema)) return schema
if (TypeGuard.TLiteralUnion(schema)) return schema.anyOf.map((schema) => schema.const)
if (TypeGuard.TLiteralUnion(schema)) return schema.anyOf.map((schema) => schema.const.toString())
if (TypeGuard.TLiteral(schema)) return [schema.const as string]
if (TypeGuard.TTemplateLiteral(schema)) {
const expression = TemplateLiteralParser.ParseExact(schema.pattern)
Expand All @@ -2030,6 +2030,24 @@ export namespace KeyArrayResolver {
}
}
// --------------------------------------------------------------------------
// UnionResolver
// --------------------------------------------------------------------------
export namespace UnionResolver {
function* Union(union: TUnion): IterableIterator<TSchema> {
for (const schema of union.anyOf) {
if (schema[Kind] === 'Union') {
yield* Union(schema as TUnion)
} else {
yield schema
}
}
}
/** Returns a resolved union with interior unions flattened */
export function Resolve(union: TUnion): TUnion {
return Type.Union([...Union(union)], { ...union })
}
}
// --------------------------------------------------------------------------
// TemplateLiteralPattern
// --------------------------------------------------------------------------
export namespace TemplateLiteralPattern {
Expand Down Expand Up @@ -2389,11 +2407,11 @@ export class StandardTypeBuilder extends TypeBuilder {
}
}
/** `[Standard]` Returns indexed property types for the given keys */
public Index<T extends TSchema, K extends (keyof Static<T>)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex<T, K>
public Index<T extends TSchema, K extends (keyof Static<T>)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex<T, Assert<K, Key[]>>
/** `[Standard]` Returns indexed property types for the given keys */
public Index<T extends TSchema, K extends TUnion<TLiteral<string | number>[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex<T, TLiteralUnion<K>>
public Index<T extends TSchema, K extends TUnion<TLiteral<Key>[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex<T, TLiteralUnion<K>>
/** `[Standard]` Returns indexed property types for the given keys */
public Index<T extends TSchema, K extends TLiteral<string | number>>(schema: T, key: K, options?: SchemaOptions): TIndex<T, [K['const']]>
public Index<T extends TSchema, K extends TLiteral<Key>>(schema: T, key: K, options?: SchemaOptions): TIndex<T, [K['const']]>
/** `[Standard]` Returns indexed property types for the given keys */
public Index<T extends TSchema, K extends TTemplateLiteral>(schema: T, key: K, options?: SchemaOptions): TIndex<T, TTemplateLiteralKeyArray<K>>
/** `[Standard]` Returns indexed property types for the given keys */
Expand Down Expand Up @@ -2559,7 +2577,7 @@ export class StandardTypeBuilder extends TypeBuilder {
}, options)
}
/** `[Standard]` Creates a Record type */
public Record<K extends TUnion<TLiteral<string | number>[]>, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): RecordUnionLiteralType<K, T>
public Record<K extends TUnion, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): RecordUnionLiteralType<K, T>
/** `[Standard]` Creates a Record type */
public Record<K extends TLiteral<string | number>, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): RecordLiteralType<K, T>
/** `[Standard]` Creates a Record type */
Expand All @@ -2576,23 +2594,24 @@ export class StandardTypeBuilder extends TypeBuilder {
return TemplateLiteralFinite.Check(expression)
? (this.Object([...TemplateLiteralGenerator.Generate(expression)].reduce((acc, key) => ({ ...acc, [key]: TypeClone.Clone(schema, {}) }), {} as TProperties), options))
: this.Create<any>({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [key.pattern]: TypeClone.Clone(schema, {}) }})
} else if (TypeGuard.TLiteralUnion(key)) {
if (key.anyOf.every((schema) => TypeGuard.TLiteral(schema) && (typeof schema.const === 'string' || typeof schema.const === 'number'))) {
const properties = key.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Clone(schema, {}) }), {} as TProperties)
} else if (TypeGuard.TUnion(key)) {
const union = UnionResolver.Resolve(key)
if (TypeGuard.TLiteralUnion(union)) {
const properties = union.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Clone(schema, {}) }), {} as TProperties)
return this.Object(properties, { ...options, [Hint]: 'Record' })
} else throw Error('TypeBuilder: Record key can only be derived from union literal of number or string')
} else throw Error('TypeBuilder: Record key of type union contains non-literal types')
} else if (TypeGuard.TLiteral(key)) {
if (typeof key.const === 'string' || typeof key.const === 'number') {
return this.Object({ [key.const]: TypeClone.Clone(schema, {}) }, options)
} else throw Error('TypeBuilder: Record key can only be derived from literals of number or string')
} else throw Error('TypeBuilder: Record key of type literal is not of type string or number')
} else if (TypeGuard.TInteger(key) || TypeGuard.TNumber(key)) {
const pattern = PatternNumberExact
return this.Create<any>({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema, {}) } })
} else if (TypeGuard.TString(key)) {
const pattern = key.pattern === undefined ? PatternStringExact : key.pattern
return this.Create<any>({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema, {}) } })
} else {
throw Error(`StandardTypeBuilder: Invalid Record Key`)
throw Error(`StandardTypeBuilder: Record key is an invalid type`)
}
}
/** `[Standard]` Creates a Recursive type */
Expand Down
Loading

0 comments on commit 11b09a1

Please sign in to comment.