From 2cf0546b425db27d2a8a983952995ab39f0e44bd Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 28 Nov 2023 17:36:48 +0900 Subject: [PATCH] Esm --- src/type/helpers/helpers.ts | 23 +- src/type/increment/increment.ts | 39 --- src/type/index.ts | 5 +- src/type/intrinsic/intrinsic.ts | 222 ++++++++--------- src/type/keyof/keyof-string.ts | 19 +- src/type/logic/index.ts | 29 --- src/type/logic/logic.ts | 204 ---------------- src/type/omit/omit.ts | 114 ++++----- src/type/{increment => operators}/index.ts | 2 +- src/type/operators/operators.ts | 224 ++++++++++++++++++ src/type/template-literal/pattern.ts | 53 +++-- src/type/template-literal/syntax.ts | 145 ++++++------ src/type/template-literal/template-literal.ts | 6 +- test/runtime/type/intrinsic/intrinsic.ts | 54 ++--- 14 files changed, 562 insertions(+), 577 deletions(-) delete mode 100644 src/type/increment/increment.ts delete mode 100644 src/type/logic/index.ts delete mode 100644 src/type/logic/logic.ts rename src/type/{increment => operators}/index.ts (97%) create mode 100644 src/type/operators/operators.ts diff --git a/src/type/helpers/helpers.ts b/src/type/helpers/helpers.ts index 7f38188e..9d97a3e9 100644 --- a/src/type/helpers/helpers.ts +++ b/src/type/helpers/helpers.ts @@ -30,7 +30,7 @@ import type { TSchema } from '../schema/index' import type { TProperties } from '../object/index' import type { TNever } from '../never/index' // ------------------------------------------------------------------ -// Helper: Core +// Helper: Common // ------------------------------------------------------------------ export type TupleToIntersect = T extends [infer I] ? I : T extends [infer I, ...infer R] ? I & TupleToIntersect : never export type TupleToUnion = { [K in keyof T]: T[K] }[number] @@ -43,9 +43,26 @@ export type Evaluate = T extends infer O ? { [K in keyof O]: O[K] } : never export type Ensure = T extends infer U ? U : never export type EmptyString = '' export type ZeroString = '0' - // ------------------------------------------------------------------ -// Helper: Asserts +// Helper: Increment +// ------------------------------------------------------------------ +export type IncrementBase = { m: '9'; t: '01'; '0': '1'; '1': '2'; '2': '3'; '3': '4'; '4': '5'; '5': '6'; '6': '7'; '7': '8'; '8': '9'; '9': '0' } +export type IncrementTake = IncrementBase[T] +export type IncrementStep = T extends IncrementBase['m'] + ? IncrementBase['t'] + : T extends `${infer L extends keyof IncrementBase}${infer R}` + ? L extends IncrementBase['m'] + ? `${IncrementTake}${IncrementStep}` + : `${IncrementTake}${R}` + : never +export type IncrementReverse = T extends `${infer L}${infer R}` ? `${IncrementReverse}${L}` : T +export type Increment = IncrementReverse>> +/** Increments the given string value + 1 */ +export function Increment(T: T): Increment { + return (parseInt(T) + 1).toString() as Increment +} +// ------------------------------------------------------------------ +// Helper: Type Asserts // ------------------------------------------------------------------ export type AssertProperties = T extends TProperties ? T : TProperties export type AssertRest = T extends E ? T : [] diff --git a/src/type/increment/increment.ts b/src/type/increment/increment.ts deleted file mode 100644 index ecc6bffe..00000000 --- a/src/type/increment/increment.ts +++ /dev/null @@ -1,39 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export namespace Increment { - export type Base = { m: '9'; t: '01'; '0': '1'; '1': '2'; '2': '3'; '3': '4'; '4': '5'; '5': '6'; '6': '7'; '7': '8'; '8': '9'; '9': '0' } - export type Take = Base[T] - export type Step = T extends Base['m'] ? Base['t'] : T extends `${infer L extends keyof Base}${infer R}` ? (L extends Base['m'] ? `${Take}${Step}` : `${Take}${R}`) : never - export type Reverse = T extends `${infer L}${infer R}` ? `${Reverse}${L}` : T - export type Next = Reverse>> - /** Increments the given string value + 1 */ - export function Next(T: T): Next { - return (parseInt(T) + 1).toString() as Next - } -} diff --git a/src/type/index.ts b/src/type/index.ts index a270ca86..115d0538 100644 --- a/src/type/index.ts +++ b/src/type/index.ts @@ -85,8 +85,8 @@ THE SOFTWARE. // export { type TSchema } from './schema/index' // export { type Static, type StaticDecode, type StaticEncode } from './static/index' // export { type TString, String } from './string/index' - // export { type TSymbol, Symbol } from './symbol/index' + // export { type TTemplateLiteral, type TTemplateLiteralKind, TemplateLiteral } from './template-literal/index' // export { type TTransform, Transform } from './transform/index' // export { type TTuple, Tuple } from './tuple/index' @@ -119,7 +119,6 @@ export * from './extract/index' export * from './function/index' export * from './guard/index' export * from './helpers/index' -export * from './increment/index' export * from './indexed/index' export * from './instance-type/index' export * from './integer/index' @@ -128,7 +127,6 @@ export * from './intrinsic/index' export * from './iterator/index' export * from './keyof/index' export * from './literal/index' -export * from './logic/index' export * from './modifiers/index' export * from './never/index' export * from './not/index' @@ -136,6 +134,7 @@ export * from './null/index' export * from './number/index' export * from './object/index' export * from './omit/index' +export * from './operators/index' export * from './optional/index' export * from './parameters/index' export * from './partial/index' diff --git a/src/type/intrinsic/intrinsic.ts b/src/type/intrinsic/intrinsic.ts index 824a6a07..fd0b78b2 100644 --- a/src/type/intrinsic/intrinsic.ts +++ b/src/type/intrinsic/intrinsic.ts @@ -30,142 +30,146 @@ import type { TSchema, SchemaOptions } from '../schema/index' import { type TTemplateLiteral, type TTemplateLiteralKind, TemplateLiteral, TemplateLiteralParseExact, IsTemplateLiteralFinite, TemplateLiteralGenerate } from '../template-literal/index' import { type TLiteral, type TLiteralValue, Literal } from '../literal/index' import { type TUnion, Union } from '../union/index' -import { CloneType } from '../clone/type' import { TTemplateLiteral as IsTemplateLiteralType, TUnion as IsUnionType, TLiteral as IsLiteralType } from '../guard/type' +import { CloneType } from '../clone/type' +// ---------------------------------------------------------------- +// Apply +// ---------------------------------------------------------------- +function ApplyUncapitalize(value: string): string { + const [first, rest] = [value.slice(0, 1), value.slice(1)] + return [first.toLowerCase(), rest].join('') +} +function ApplyCapitalize(value: string): string { + const [first, rest] = [value.slice(0, 1), value.slice(1)] + return [first.toUpperCase(), rest].join('') +} +function ApplyUppercase(value: string): string { + return value.toUpperCase() +} +function ApplyLowercase(value: string): string { + return value.toLowerCase() +} +// ---------------------------------------------------------------- +// IntrinsicMode +// ---------------------------------------------------------------- +type IntrinsicMode = 'Uppercase' | 'Lowercase' | 'Capitalize' | 'Uncapitalize' +// ---------------------------------------------------------------- +// IntrinsicTemplateLiteral +// ---------------------------------------------------------------- // prettier-ignore -export namespace IntrinsicResolver { - // ---------------------------------------------------------------- - // Operations - // ---------------------------------------------------------------- - function Uncapitalize(value: string): string { - const [first, rest] = [value.slice(0, 1), value.slice(1)] - return [first.toLowerCase(), rest].join('') - } - function Capitalize(value: string): string { - const [first, rest] = [value.slice(0, 1), value.slice(1)] - return [first.toUpperCase(), rest].join('') - } - function Uppercase(value: string): string { - return value.toUpperCase() - } - function Lowercase(value: string): string { - return value.toLowerCase() - } - // ---------------------------------------------------------------- - // IntrinsicTemplateLiteral - // ---------------------------------------------------------------- - export type IntrinsicMode = 'Uppercase' | 'Lowercase' | 'Capitalize' | 'Uncapitalize' - // ---------------------------------------------------------------- - // IntrinsicTemplateLiteral - // ---------------------------------------------------------------- - // prettier-ignore - export type FromTemplateLIteral = - M extends IntrinsicMode ? - T extends [infer L extends TTemplateLiteralKind, ...infer R extends TTemplateLiteralKind[]] - ? [Resolve, ...FromTemplateLIteral] - : T - : T - function FromTemplateLiteral(schema: TTemplateLiteral, mode: IntrinsicMode): FromTemplateLIteral { - // note: template literals require special runtime handling as they are encoded in string patterns. - // This diverges from the mapped type which would otherwise map on the template literal kind. - const expression = TemplateLiteralParseExact(schema.pattern) - const finite = IsTemplateLiteralFinite(expression) - if (!finite) return { ...schema, pattern: FromLiteralValue(schema.pattern, mode) } as any - const strings = [...TemplateLiteralGenerate(expression)] - const literals = strings.map((value) => Literal(value)) - const mapped = FromRest(literals as any, mode) - const union = Union(mapped) - return TemplateLiteral([union]) as unknown as FromTemplateLIteral - } - // ---------------------------------------------------------------- - // IntrinsicLiteral - // ---------------------------------------------------------------- - // prettier-ignore - export type FromLiteralValue = ( - T extends string ? - M extends 'Uncapitalize' ? Uncapitalize : - M extends 'Capitalize' ? Capitalize : - M extends 'Uppercase' ? Uppercase : - M extends 'Lowercase' ? Lowercase : - string +type FromTemplateLiteral = + M extends IntrinsicMode ? + T extends [infer L extends TTemplateLiteralKind, ...infer R extends TTemplateLiteralKind[]] + ? [IntrinsicResolve, ...FromTemplateLiteral] : T + : T +function FromTemplateLiteral(schema: TTemplateLiteral, mode: IntrinsicMode): FromTemplateLiteral { + // note: template literals require special runtime handling as they are encoded in string patterns. + // This diverges from the mapped type which would otherwise map on the template literal kind. + const expression = TemplateLiteralParseExact(schema.pattern) + const finite = IsTemplateLiteralFinite(expression) + if (!finite) return { ...schema, pattern: FromLiteralValue(schema.pattern, mode) } as any + const strings = [...TemplateLiteralGenerate(expression)] + const literals = strings.map((value) => Literal(value)) + const mapped = FromRest(literals as any, mode) + const union = Union(mapped) + return TemplateLiteral([union]) as unknown as FromTemplateLiteral +} +// ---------------------------------------------------------------- +// FromLiteralValue +// ---------------------------------------------------------------- +// prettier-ignore +type FromLiteralValue = ( + T extends string ? + M extends 'Uncapitalize' ? Uncapitalize : + M extends 'Capitalize' ? Capitalize : + M extends 'Uppercase' ? Uppercase : + M extends 'Lowercase' ? Lowercase : + string + : T +) +// prettier-ignore +function FromLiteralValue(value: TLiteralValue, mode: IntrinsicMode) { + return ( + typeof value === 'string' ? ( + mode === 'Uncapitalize' ? ApplyUncapitalize(value) : + mode === 'Capitalize' ? ApplyCapitalize(value) : + mode === 'Uppercase' ? ApplyUppercase(value) : + mode === 'Lowercase' ? ApplyLowercase(value) : + value + ) : value.toString() ) - function FromLiteralValue(value: TLiteralValue, mode: IntrinsicMode) { - return ( - typeof value === 'string' ? ( - mode === 'Uncapitalize' ? Uncapitalize(value) : - mode === 'Capitalize' ? Capitalize(value) : - mode === 'Uppercase' ? Uppercase(value) : - mode === 'Lowercase' ? Lowercase(value) : - value - ) : value.toString() - ) - } - // ---------------------------------------------------------------- - // FromRest - // ---------------------------------------------------------------- - // prettier-ignore - export type FromRest = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? [Resolve, ...FromRest] - : [] - function FromRest(T: [...T], mode: M): FromRest { - const [L, ...R] = T - return ( - T.length > 0 - ? [Resolve(L, mode), ...FromRest(R, mode)] - : [] - ) as FromRest - } - // ---------------------------------------------------------------- - // Resolve - // ---------------------------------------------------------------- - // prettier-ignore - export type Resolve = - T extends TTemplateLiteral ? TTemplateLiteral> : - T extends TUnion ? TUnion> : - T extends TLiteral ? TLiteral> : - T - /** Applies an intrinsic string manipulation to the given type. */ - export function Resolve(schema: T, mode: M): Resolve { - return ( - IsTemplateLiteralType(schema) ? FromTemplateLiteral(schema, mode) : - IsUnionType(schema) ? Union(FromRest(schema.anyOf, mode)) : - IsLiteralType(schema) ? Literal(FromLiteralValue(schema.const, mode)) : - schema - ) as Resolve - } } +// ---------------------------------------------------------------- +// FromRest +// ---------------------------------------------------------------- +// prettier-ignore +type FromRest = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [IntrinsicResolve, ...FromRest] + : [] +function FromRest(T: [...T], mode: M): FromRest { + const [L, ...R] = T + return (T.length > 0 ? [IntrinsicResolve(L, mode), ...FromRest(R, mode)] : []) as FromRest +} +// ---------------------------------------------------------------- +// Resolve +// ---------------------------------------------------------------- +// prettier-ignore +export type IntrinsicResolve = + T extends TTemplateLiteral ? TTemplateLiteral> : + T extends TUnion ? TUnion> : + T extends TLiteral ? TLiteral> : + T +/** Applies an intrinsic string manipulation to the given type. */ +// prettier-ignore +export function IntrinsicResolve(schema: T, mode: M): IntrinsicResolve { + return ( + IsTemplateLiteralType(schema) ? FromTemplateLiteral(schema, mode) : + IsUnionType(schema) ? Union(FromRest(schema.anyOf, mode)) : + IsLiteralType(schema) ? Literal(FromLiteralValue(schema.const, mode)) : + schema + ) as IntrinsicResolve +} + // ------------------------------------------------------------------ // TUncapitalize // ------------------------------------------------------------------ -export type TUncapitalize = IntrinsicResolver.Resolve +// prettier-ignore +export type TUncapitalize = IntrinsicResolve /** `[Json]` Intrinsic function to Uncapitalize LiteralString types */ +// prettier-ignore export function Uncapitalize(T: T, options: SchemaOptions = {}): TUncapitalize { - return CloneType(IntrinsicResolver.Resolve(T, 'Uncapitalize'), options) + return CloneType(IntrinsicResolve(T, 'Uncapitalize'), options) } // ------------------------------------------------------------------ // TUppercase // ------------------------------------------------------------------ -export type TUppercase = IntrinsicResolver.Resolve +// prettier-ignore +export type TUppercase = IntrinsicResolve /** `[Json]` Intrinsic function to Uppercase LiteralString types */ +// prettier-ignore export function Uppercase(T: T, options: SchemaOptions = {}): TUppercase { - return CloneType(IntrinsicResolver.Resolve(T, 'Uppercase'), options) + return CloneType(IntrinsicResolve(T, 'Uppercase'), options) } // ------------------------------------------------------------------ // TLowercase // ------------------------------------------------------------------ -export type TLowercase = IntrinsicResolver.Resolve +// prettier-ignore +export type TLowercase = IntrinsicResolve /** `[Json]` Intrinsic function to Lowercase LiteralString types */ +// prettier-ignore export function Lowercase(T: T, options: SchemaOptions = {}): TLowercase { - return CloneType(IntrinsicResolver.Resolve(T, 'Lowercase'), options) + return CloneType(IntrinsicResolve(T, 'Lowercase'), options) } // ------------------------------------------------------------------ // TCapitalize // ------------------------------------------------------------------ -export type TCapitalize = IntrinsicResolver.Resolve +// prettier-ignore +export type TCapitalize = IntrinsicResolve /** `[Json]` Intrinsic function to Capitalize LiteralString types */ +// prettier-ignore export function Capitalize(T: T, options: SchemaOptions = {}): TCapitalize { - return CloneType(IntrinsicResolver.Resolve(T, 'Capitalize'), options) + return CloneType(IntrinsicResolve(T, 'Capitalize'), options) } diff --git a/src/type/keyof/keyof-string.ts b/src/type/keyof/keyof-string.ts index 6c9e339b..96903a17 100644 --- a/src/type/keyof/keyof-string.ts +++ b/src/type/keyof/keyof-string.ts @@ -27,15 +27,14 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { TSchema } from '../schema/index' -import type { TObject, TProperties } from '../object/index' +import { type ZeroString, type UnionToTuple, Increment } from '../helpers/index' import type { TRecursive } from '../recursive/index' -import type { ZeroString, UnionToTuple } from '../helpers/index' import type { TIntersect } from '../intersect/index' import type { TUnion } from '../union/index' import type { TTuple } from '../tuple/index' import type { TArray } from '../array/index' -import { Increment } from '../increment/index' -import { Logic } from '../logic/index' +import type { TObject, TProperties } from '../object/index' +import { OperatorUnionMany, OperatorIntersectMany } from '../operators/index' import { TIntersect as IsIntersectType, TUnion as IsUnionType, TTuple as IsTupleType, TArray as IsArrayType, TObject as IsObjectType, TRecord as IsRecordType } from '../guard/type' // ---------------------------------------------------------------- @@ -61,12 +60,12 @@ function FromRest(T: [...T]): FromRest { // ---------------------------------------------------------------- // prettier-ignore type FromIntersect> = ( - Logic.UnionMany + OperatorUnionMany ) // prettier-ignore function FromIntersect(T: [...T], C = FromRest(T)): FromIntersect { return ( - Logic.UnionMany(C as PropertyKey[][]) as FromIntersect + OperatorUnionMany(C as PropertyKey[][]) as FromIntersect ) } // ---------------------------------------------------------------- @@ -74,13 +73,13 @@ function FromIntersect(T: [...T], C = FromRest(T)): FromInt // ---------------------------------------------------------------- // prettier-ignore type FromUnion> = ( - Logic.IntersectMany + OperatorIntersectMany ) // prettier-ignore function FromUnion(T: [...T]): FromUnion { const C = FromRest(T) as PropertyKey[][] return ( - Logic.IntersectMany(C) as FromUnion + OperatorIntersectMany(C) as FromUnion ) } // ---------------------------------------------------------------- @@ -89,14 +88,14 @@ function FromUnion(T: [...T]): FromUnion { // prettier-ignore type FromTuple = T extends [infer _ extends TSchema, ...infer R extends TSchema[]] - ? [I, ...FromTuple>] + ? [I, ...FromTuple>] : [] // prettier-ignore function FromTuple(T: [...T], I = '0'): FromTuple { const [_, ...R] = T return ( T.length > 0 - ? [I, ...FromTuple(R, Increment.Next(I))] + ? [I, ...FromTuple(R, Increment(I))] : [] ) as FromTuple } diff --git a/src/type/logic/index.ts b/src/type/logic/index.ts deleted file mode 100644 index 3ca9b944..00000000 --- a/src/type/logic/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export * from './logic' diff --git a/src/type/logic/logic.ts b/src/type/logic/logic.ts deleted file mode 100644 index cef9c4d7..00000000 --- a/src/type/logic/logic.ts +++ /dev/null @@ -1,204 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -// prettier-ignore -export namespace Logic { - // ---------------------------------------------------------------- - // Includes - // ---------------------------------------------------------------- - export type Includes = ( - T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? S extends L - ? true - : Includes - : false - ) - /** Returns true if element S is in the set of T */ - export function Includes(T: [...T], S: S): Includes { - const [L, ...R] = T - return ( - T.length > 0 - ? S === L - ? true - : Includes(R, S) - : false - ) as Includes - } - // ---------------------------------------------------------------- - // Subset - // ---------------------------------------------------------------- - export type Subset = - T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? Logic.Includes extends true - ? Subset - : false - : true - - /** Returns true if T is a subset of S */ - export function Subset(T: [...T], S: [...S]): Subset { - const [L, ...R] = T - return ( - T.length > 0 - ? Logic.Includes(S, L) === true - ? Subset(R, S) - : false - : true - ) as Subset - } - // ---------------------------------------------------------------- - // Distinct - // ---------------------------------------------------------------- - export type Distinct = - T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? Includes extends false - ? Distinct - : Distinct - : Acc - /** Returns a distinct set of elements */ - export function Distinct(T: [...T], Acc: PropertyKey[] = []): Distinct { - const [L, ...R] = T - return ( - T.length > 0 - ? Includes(Acc, L) === false - ? Distinct(R, [...Acc, L]) - : Distinct(R, [...Acc]) - : Acc - ) as Distinct - } - // ---------------------------------------------------------------- - // Intersect - // ---------------------------------------------------------------- - export type Intersect = ( - T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? Includes extends true - ? [L, ...Intersect] - : [...Intersect] - : [] - ) - /** Returns the Intersect of the given sets */ - export function Intersect(T: [...T], S: [...S]): Intersect { - const [L, ...R] = T - return ( - T.length > 0 - ? Includes(S, L) === true - ? [L, ...Intersect(R, S)] - : [...Intersect(R, S)] - : [] - ) as Intersect - } - // ---------------------------------------------------------------- - // Union - // ---------------------------------------------------------------- - export type Union = ( - T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? Includes extends true - ? [...Union] - : [L, ...Union] - : S - ) - /** Returns the Union of the given sets */ - export function Union(T: [...T], S: [...S]): Union { - const [L, ...R] = T - return ( - T.length > 0 - ? Includes(S, L) === true - ? [...Union(R, S)] - : [L, ...Union(R, S)] - : S - ) as Union - } - // ---------------------------------------------------------------- - // Complement - // ---------------------------------------------------------------- - export type Complement = ( - T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? Logic.Includes extends true - ? [...Complement] - : [L, ...Complement] - : [] - ) - /** Returns the Complement by omitting elements in T that are in S */ - export function Complement(T: [...T], S: [...S]): Complement { - const [L, ...R] = T - return ( - T.length > 0 - ? Logic.Includes(S, L) === true - ? [...Complement(R, S)] - : [L, ...Complement(R, S)] - : [] - ) as Complement - } - // ---------------------------------------------------------------- - // IntersectMany - // ---------------------------------------------------------------- - export type IntersectMany = ( - T extends [infer L extends PropertyKey[]] - ? L - : T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] - ? Intersect> - : [] - ) - /** Returns the Intersect of multiple sets */ - export function IntersectMany(T: [...T]): IntersectMany { - return ( - T.length === 1 - ? T[0] - : (() => { - const [L, ...R] = T - return ( - L.length > 0 - ? Intersect(L, IntersectMany(R)) - : [] - ) - })() - ) as IntersectMany - } - // ---------------------------------------------------------------- - // UnionMany - // ---------------------------------------------------------------- - export type UnionMany = ( - T extends [infer L extends PropertyKey[]] - ? L - : T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] - ? Union> - : [] - ) - /** Returns the Union of multiple sets */ - export function UnionMany(T: [...T]): UnionMany { - return ( - T.length === 1 - ? T[0] - : (() => { - const [L, ...R] = T - return T.length > 0 - ? Union(L, UnionMany(R)) - : [] - })() - ) as UnionMany - } -} diff --git a/src/type/omit/omit.ts b/src/type/omit/omit.ts index 693afb6e..51afaa94 100644 --- a/src/type/omit/omit.ts +++ b/src/type/omit/omit.ts @@ -38,64 +38,72 @@ import { Discard } from '../discard/index' import { Symbols } from '../symbols/index' import { CloneType } from '../clone/type' import { TIntersect as IsIntersectType, TUnion as IsUnionType, TObject as IsObjectType, TSchema as IsSchemaType } from '../guard/type' + +// ---------------------------------------------------------------- +// FromIntersect +// ---------------------------------------------------------------- +// prettier-ignore +type FromIntersect = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [OmitResolve, ...FromIntersect] + : [] +// prettier-ignore +function FromIntersect(T: T, K: K) { + return T.map((T) => OmitResolve(T, K)) as FromIntersect +} +// ---------------------------------------------------------------- +// FromUnion +// ---------------------------------------------------------------- +// prettier-ignore +type FromUnion = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [OmitResolve, ...FromUnion] + : [] +// prettier-ignore +function FromUnion(T: T, K: K) { + return T.map((T) => OmitResolve(T, K)) as FromUnion +} +// ---------------------------------------------------------------- +// FromProperty +// ---------------------------------------------------------------- // prettier-ignore -export namespace OmitResolver { - // ---------------------------------------------------------------- - // IntersectRest - // ---------------------------------------------------------------- - export type IntersectRest = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? [Resolve, ...IntersectRest] - : [] - export function IntersectRest(T: T, K: K) { - return T.map((T) => Resolve(T, K)) as IntersectRest - } - // ---------------------------------------------------------------- - // UnionRest - // ---------------------------------------------------------------- - export type UnionRest = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? [Resolve, ...UnionRest] - : [] - export function UnionRest(T: T, K: K) { - return T.map((T) => Resolve(T, K)) as UnionRest - } - // ---------------------------------------------------------------- - // Properties - // ---------------------------------------------------------------- - export function Property, K extends PropertyKey>(T: T, K: K) { - const { [K]: _, ...R } = T - return R as TProperties - } - export type Properties> = Evaluate> - export function Properties(T: T, K: K) { - return K.reduce((T, K2) => { - return Property(T, K2) - }, T as TProperties) - } - // ---------------------------------------------------------------- - // Resolve - // ---------------------------------------------------------------- - export type Resolve = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - TObject<{}> - export function Resolve(T: T, K: [...K]): Resolve { - return ( - IsIntersectType(T) ? Intersect(IntersectRest(T.allOf, K)) : - IsUnionType(T) ? Union(UnionRest(T.anyOf, K)) : - IsObjectType(T) ? Object(Properties(T.properties, K)) : - Object({}) - ) as Resolve - } +function FromProperty, K extends PropertyKey>(T: T, K: K) { + const { [K]: _, ...R } = T + return R as TProperties +} +// prettier-ignore +type FromProperties> = Evaluate> +// prettier-ignore +function FromProperties(T: T, K: K) { + return K.reduce((T, K2) => { + return FromProperty(T, K2) + }, T as TProperties) +} +// ---------------------------------------------------------------- +// Resolve +// ---------------------------------------------------------------- +// prettier-ignore +export type OmitResolve = + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + TObject<{}> +// prettier-ignore +export function OmitResolve(T: T, K: [...K]): OmitResolve { + return ( + IsIntersectType(T) ? Intersect(FromIntersect(T.allOf, K)) : + IsUnionType(T) ? Union(FromUnion(T.anyOf, K)) : + IsObjectType(T) ? Object(FromProperties(T.properties, K)) : + Object({}) + ) as OmitResolve } // ------------------------------------------------------------------ // TOmit // ------------------------------------------------------------------ -export type TOmit = OmitResolver.Resolve +// prettier-ignore +export type TOmit = OmitResolve /** `[Json]` Constructs a type whose keys are omitted from the given type */ export function Omit>(T: T, K: K, options?: SchemaOptions): TOmit @@ -104,6 +112,6 @@ export function Omit(T: T, K: readon export function Omit(T: TSchema, K: TSchema | readonly PropertyKey[], options: SchemaOptions = {}): any { const I = IsSchemaType(K) ? IndexedKeyResolve(K) : (K as string[]) const D = Discard(T, [Symbols.Transform, '$id', 'required']) as TSchema - const R = CloneType(OmitResolver.Resolve(T, I), options) + const R = CloneType(OmitResolve(T, I), options) return { ...D, ...R } } diff --git a/src/type/increment/index.ts b/src/type/operators/index.ts similarity index 97% rename from src/type/increment/index.ts rename to src/type/operators/index.ts index 4d907fc6..aa6325ea 100644 --- a/src/type/increment/index.ts +++ b/src/type/operators/index.ts @@ -26,4 +26,4 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './increment' +export * from './operators' diff --git a/src/type/operators/operators.ts b/src/type/operators/operators.ts new file mode 100644 index 00000000..29ad0899 --- /dev/null +++ b/src/type/operators/operators.ts @@ -0,0 +1,224 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// +// Operators (Set-Theory) +// +// The following functions perform common set-theory operations +// across arrays of PropertyKey. These operators are used in +// Intersect and Union composition. +// +// ------------------------------------------------------------------ + +// ------------------------------------------------------------------ +// OperatorIncludes +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorIncludes = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? S extends L + ? true + : OperatorIncludes + : false +) +/** Returns true if element S is in the set of T */ +// prettier-ignore +export function OperatorIncludes(T: [...T], S: S): OperatorIncludes { + const [L, ...R] = T + return ( + T.length > 0 + ? S === L + ? true + : OperatorIncludes(R, S) + : false + ) as OperatorIncludes +} +// ------------------------------------------------------------------ +// OperatorSubset +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorSubset = + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? OperatorIncludes extends true + ? OperatorSubset + : false + : true + +/** Returns true if T is a subset of S */ +// prettier-ignore +export function OperatorSubset(T: [...T], S: [...S]): OperatorSubset { + const [L, ...R] = T + return ( + T.length > 0 + ? OperatorIncludes(S, L) === true + ? OperatorSubset(R, S) + : false + : true + ) as OperatorSubset +} +// ------------------------------------------------------------------ +// OperatorDistinct +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorDistinct = + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? OperatorIncludes extends false + ? OperatorDistinct + : OperatorDistinct + : Acc +/** Returns a distinct set of elements */ +// prettier-ignore +export function OperatorDistinct(T: [...T], Acc: PropertyKey[] = []): OperatorDistinct { + const [L, ...R] = T + return ( + T.length > 0 + ? OperatorIncludes(Acc, L) === false + ? OperatorDistinct(R, [...Acc, L]) + : OperatorDistinct(R, [...Acc]) + : Acc + ) as OperatorDistinct +} +// ------------------------------------------------------------------ +// OperatorIntersect +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorIntersect = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? OperatorIncludes extends true + ? [L, ...OperatorIntersect] + : [...OperatorIntersect] + : [] +) +/** Returns the Intersect of the given sets */ +// prettier-ignore +export function OperatorIntersect(T: [...T], S: [...S]): OperatorIntersect { + const [L, ...R] = T + return ( + T.length > 0 + ? OperatorIncludes(S, L) === true + ? [L, ...OperatorIntersect(R, S)] + : [...OperatorIntersect(R, S)] + : [] + ) as OperatorIntersect +} +// ------------------------------------------------------------------ +// OperatorUnion +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorUnion = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? OperatorIncludes extends true + ? [...OperatorUnion] + : [L, ...OperatorUnion] + : S +) +/** Returns the Union of the given sets */ +// prettier-ignore +export function OperatorUnion(T: [...T], S: [...S]): OperatorUnion { + const [L, ...R] = T + return ( + T.length > 0 + ? OperatorIncludes(S, L) === true + ? [...OperatorUnion(R, S)] + : [L, ...OperatorUnion(R, S)] + : S + ) as OperatorUnion +} +// ------------------------------------------------------------------ +// OperatorComplement +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorComplement = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? OperatorIncludes extends true + ? [...OperatorComplement] + : [L, ...OperatorComplement] + : [] +) +/** Returns the Complement by omitting elements in T that are in S */ +// prettier-ignore +export function OperatorComplement(T: [...T], S: [...S]): OperatorComplement { + const [L, ...R] = T + return ( + T.length > 0 + ? OperatorIncludes(S, L) === true + ? [...OperatorComplement(R, S)] + : [L, ...OperatorComplement(R, S)] + : [] + ) as OperatorComplement +} +// ------------------------------------------------------------------ +// OperatorIntersectMany +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorIntersectMany = ( + T extends [infer L extends PropertyKey[]] + ? L + : T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] + ? OperatorIntersect> + : [] +) +/** Returns the Intersect of multiple sets */ +// prettier-ignore +export function OperatorIntersectMany(T: [...T]): OperatorIntersectMany { + return ( + T.length === 1 + ? T[0] + : (() => { + const [L, ...R] = T + return ( + L.length > 0 + ? OperatorIntersect(L, OperatorIntersectMany(R)) + : [] + ) + })() + ) as OperatorIntersectMany +} +// ------------------------------------------------------------------ +// OperatorUnionMany +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorUnionMany = ( + T extends [infer L extends PropertyKey[]] + ? L + : T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] + ? OperatorUnion> + : [] +) +/** Returns the Union of multiple sets */ +export function OperatorUnionMany(T: [...T]): OperatorUnionMany { + return ( + T.length === 1 + ? T[0] + : (() => { + const [L, ...R] = T + return T.length > 0 ? OperatorUnion(L, OperatorUnionMany(R)) : [] + })() + ) as OperatorUnionMany +} diff --git a/src/type/template-literal/pattern.ts b/src/type/template-literal/pattern.ts index bb778670..6b0a9d42 100644 --- a/src/type/template-literal/pattern.ts +++ b/src/type/template-literal/pattern.ts @@ -26,8 +26,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import type { TTemplateLiteralKind } from '../template-literal/index' import type { TSchema } from '../schema/index' +import type { TTemplateLiteralKind } from '../template-literal/index' import { PatternNumber, PatternString, PatternBoolean } from '../patterns/index' import { Symbols } from '../symbols/index' import { @@ -46,28 +46,31 @@ import { // ------------------------------------------------------------------ export class TemplateLiteralPatternError extends Error {} -// prettier-ignore -export namespace TemplateLiteralPattern { - function Throw(message: string): never { - throw new TemplateLiteralPatternError(message) - } - function Escape(value: string) { - return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') - } - function Visit(schema: TSchema, acc: string): string { - return ( - IsTemplateLiteralType(schema) ? schema.pattern.slice(1, schema.pattern.length - 1) : - IsUnionType(schema) ? `(${schema.anyOf.map((schema) => Visit(schema, acc)).join('|')})` : - IsNumberType(schema) ? `${acc}${PatternNumber}` : - IsIntegerType(schema) ? `${acc}${PatternNumber}` : - IsBigIntType(schema) ? `${acc}${PatternNumber}` : - IsStringType(schema) ? `${acc}${PatternString}` : - IsLiteralType(schema) ? `${acc}${Escape(schema.const.toString())}` : - IsBooleanType(schema) ? `${acc}${PatternBoolean}` : - Throw(`Unexpected Kind '${schema[Symbols.Kind]}'`) - ) - } - export function Create(kinds: TTemplateLiteralKind[]): string { - return `^${kinds.map((schema) => Visit(schema, '')).join('')}\$` - } +function Throw(message: string): never { + throw new TemplateLiteralPatternError(message) +} +function Escape(value: string) { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +} +function Visit(schema: TSchema, acc: string): string { + return IsTemplateLiteralType(schema) + ? schema.pattern.slice(1, schema.pattern.length - 1) + : IsUnionType(schema) + ? `(${schema.anyOf.map((schema) => Visit(schema, acc)).join('|')})` + : IsNumberType(schema) + ? `${acc}${PatternNumber}` + : IsIntegerType(schema) + ? `${acc}${PatternNumber}` + : IsBigIntType(schema) + ? `${acc}${PatternNumber}` + : IsStringType(schema) + ? `${acc}${PatternString}` + : IsLiteralType(schema) + ? `${acc}${Escape(schema.const.toString())}` + : IsBooleanType(schema) + ? `${acc}${PatternBoolean}` + : Throw(`Unexpected Kind '${schema[Symbols.Kind]}'`) +} +export function TemplateLiteralPattern(kinds: TTemplateLiteralKind[]): string { + return `^${kinds.map((schema) => Visit(schema, '')).join('')}\$` } diff --git a/src/type/template-literal/syntax.ts b/src/type/template-literal/syntax.ts index 46034907..aa4d7d51 100644 --- a/src/type/template-literal/syntax.ts +++ b/src/type/template-literal/syntax.ts @@ -36,82 +36,85 @@ import { type TString, String } from '../string/index' import { Union, UnionResolve } from '../union/index' import { Never } from '../never/index' +// ------------------------------------------------------------------------- +// Parser +// ------------------------------------------------------------------------- // prettier-ignore -export namespace TemplateLiteralSyntax { - // ------------------------------------------------------------------------- - // Runtime Parse - // ------------------------------------------------------------------------- - function* ParseUnion(template: string): IterableIterator { - const trim = template.trim().replace(/"|'/g, '') - return ( - trim === 'boolean' ? yield Boolean() : - trim === 'number' ? yield Number() : - trim === 'bigint' ? yield BigInt() : - trim === 'string' ? yield String() : - yield (() => { - const literals = trim.split('|').map((literal) => Literal(literal.trim())) - return ( - literals.length === 0 ? Never() : - literals.length === 1 ? literals[0] : - Union(literals) - ) - })() - ) +function* FromUnion(syntax: string): IterableIterator { + const trim = syntax.trim().replace(/"|'/g, '') + return ( + trim === 'boolean' ? yield Boolean() : + trim === 'number' ? yield Number() : + trim === 'bigint' ? yield BigInt() : + trim === 'string' ? yield String() : + yield (() => { + const literals = trim.split('|').map((literal) => Literal(literal.trim())) + return ( + literals.length === 0 ? Never() : + literals.length === 1 ? literals[0] : + Union(literals) + ) + })() + ) +} +// prettier-ignore +function* FromTerminal(syntax: string): IterableIterator { + if (syntax[1] !== '{') { + const L = Literal('$') + const R = FromSyntax(syntax.slice(1)) + return yield* [L, ...R] } - function* ParseTerminal(template: string): IterableIterator { - if (template[1] !== '{') { - const L = Literal('$') - const R = ParseLiteral(template.slice(1)) - return yield* [L, ...R] - } - for (let i = 2; i < template.length; i++) { - if (template[i] === '}') { - const L = ParseUnion(template.slice(2, i)) - const R = ParseLiteral(template.slice(i + 1)) - return yield* [...L, ...R] - } + for (let i = 2; i < syntax.length; i++) { + if (syntax[i] === '}') { + const L = FromUnion(syntax.slice(2, i)) + const R = FromSyntax(syntax.slice(i + 1)) + return yield* [...L, ...R] } - yield Literal(template) } - function* ParseLiteral(template: string): IterableIterator { - for (let i = 0; i < template.length; i++) { - if (template[i] === '$') { - const L = Literal(template.slice(0, i)) - const R = ParseTerminal(template.slice(i)) - return yield* [L, ...R] - } + yield Literal(syntax) +} +// prettier-ignore +function* FromSyntax(syntax: string): IterableIterator { + for (let i = 0; i < syntax.length; i++) { + if (syntax[i] === '$') { + const L = Literal(syntax.slice(0, i)) + const R = FromTerminal(syntax.slice(i)) + return yield* [L, ...R] } - yield Literal(template) } - // ------------------------------------------------------------------------- - // Static Parse - // ------------------------------------------------------------------------- - // prettier-ignore - export type UnionLiteral = - T extends `${infer L}|${infer R}` ? [TLiteral>, ...UnionLiteral] : - T extends `${infer L}` ? [TLiteral>] : - [] - export type Union = UnionResolve> - // prettier-ignore - export type Terminal = - T extends 'boolean' ? TBoolean : - T extends 'bigint' ? TBigInt : - T extends 'number' ? TNumber : - T extends 'string' ? TString : - Union - // prettier-ignore - export type Template = - T extends `{${infer L}}${infer R}` ? [Terminal, ...Template] : - T extends `${infer L}$${infer R}` ? [TLiteral, ...Template] : - T extends `${infer L}` ? [TLiteral] : - [] - - // ---------------------------------------------------------------- - // Resolve - // ---------------------------------------------------------------- - export type Resolve = Ensure, TTemplateLiteralKind[]>>> + yield Literal(syntax) +} +// ------------------------------------------------------------------------- +// Static Parse +// ------------------------------------------------------------------------- +// prettier-ignore +export type FromUnionLiteral = + T extends `${infer L}|${infer R}` ? [TLiteral>, ...FromUnionLiteral] : + T extends `${infer L}` ? [TLiteral>] : + [] +export type FromUnion = UnionResolve> +// prettier-ignore +export type FromTerminal = + T extends 'boolean' ? TBoolean : + T extends 'bigint' ? TBigInt : + T extends 'number' ? TNumber : + T extends 'string' ? TString : + FromUnion +// prettier-ignore +export type FromString = + T extends `{${infer L}}${infer R}` ? [FromTerminal, ...FromString] : + T extends `${infer L}$${infer R}` ? [TLiteral, ...FromString] : + T extends `${infer L}` ? [TLiteral] : + [] - export function Resolve(template_dsl: string): TTemplateLiteralKind[] { - return [...ParseLiteral(template_dsl)] - } +// ---------------------------------------------------------------- +// TemplateLiteralSyntax +// ---------------------------------------------------------------- +// prettier-ignore +export type TemplateLiteralSyntax = Ensure, TTemplateLiteralKind[]>>> + +// prettier-ignore +/** Parses TemplateLiteralSyntax and returns a tuple of TemplateLiteralKinds */ +export function TemplateLiteralSyntax(syntax: string): TTemplateLiteralKind[] { + return [...FromSyntax(syntax)] } diff --git a/src/type/template-literal/template-literal.ts b/src/type/template-literal/template-literal.ts index 013b1742..832863e3 100644 --- a/src/type/template-literal/template-literal.ts +++ b/src/type/template-literal/template-literal.ts @@ -86,14 +86,14 @@ export interface TTemplateLiteral(syntax: T, options?: SchemaOptions): TemplateLiteralSyntax.Resolve +export function TemplateLiteral(syntax: T, options?: SchemaOptions): TemplateLiteralSyntax /** `[Json]` Creates a TemplateLiteral type */ export function TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral /** `[Json]` Creates a TemplateLiteral type */ // prettier-ignore export function TemplateLiteral(unresolved: TTemplateLiteralKind[] | string, options: SchemaOptions = {}) { const pattern = IsString(unresolved) - ? TemplateLiteralPattern.Create(TemplateLiteralSyntax.Resolve(unresolved)) - : TemplateLiteralPattern.Create(unresolved as TTemplateLiteralKind[]) + ? TemplateLiteralPattern(TemplateLiteralSyntax(unresolved)) + : TemplateLiteralPattern(unresolved as TTemplateLiteralKind[]) return { ...options, [Symbols.Kind]: 'TemplateLiteral', type: 'string', pattern } } diff --git a/test/runtime/type/intrinsic/intrinsic.ts b/test/runtime/type/intrinsic/intrinsic.ts index 95158889..5cf3346c 100644 --- a/test/runtime/type/intrinsic/intrinsic.ts +++ b/test/runtime/type/intrinsic/intrinsic.ts @@ -1,22 +1,22 @@ import { TypeGuard } from '@sinclair/typebox' -import { IntrinsicResolver } from '@sinclair/typebox/type' +import { IntrinsicResolve } from '@sinclair/typebox/type' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/intrinsic/Map', () => { +describe('type/intrinsic/IntrinsicResolve', () => { // ---------------------------------------------------- // Passthrough // ---------------------------------------------------- it('Should passthrough 1', () => { - const T = IntrinsicResolver.Resolve(Type.String(), 'Capitalize') + const T = IntrinsicResolve(Type.String(), 'Capitalize') Assert.IsTrue(TypeGuard.TString(T)) }) it('Should passthrough 2', () => { - const T = IntrinsicResolver.Resolve(Type.Number(), 'Capitalize') + const T = IntrinsicResolve(Type.Number(), 'Capitalize') Assert.IsTrue(TypeGuard.TNumber(T)) }) it('Should passthrough 3', () => { - const T = IntrinsicResolver.Resolve( + const T = IntrinsicResolve( Type.Object({ x: Type.Number(), }), @@ -31,7 +31,7 @@ describe('type/intrinsic/Map', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Literal('hello') const U = Type.Union([A, B]) - const T = IntrinsicResolver.Resolve(U, 'Capitalize') + const T = IntrinsicResolve(U, 'Capitalize') Assert.IsTrue(TypeGuard.TUnion(T)) Assert.IsTrue(TypeGuard.TObject(T.anyOf[0])) Assert.IsTrue(TypeGuard.TLiteral(T.anyOf[1])) @@ -41,22 +41,22 @@ describe('type/intrinsic/Map', () => { // Mode: Literal // ---------------------------------------------------- it('Should map literal: Capitalize', () => { - const T = IntrinsicResolver.Resolve(Type.Literal('hello'), 'Capitalize') + const T = IntrinsicResolve(Type.Literal('hello'), 'Capitalize') Assert.IsTrue(TypeGuard.TLiteral(T)) Assert.IsEqual(T.const, 'Hello') }) it('Should map literal: Uncapitalize', () => { - const T = IntrinsicResolver.Resolve(Type.Literal('HELLO'), 'Uncapitalize') + const T = IntrinsicResolve(Type.Literal('HELLO'), 'Uncapitalize') Assert.IsTrue(TypeGuard.TLiteral(T)) Assert.IsEqual(T.const, 'hELLO') }) it('Should map literal: Uppercase', () => { - const T = IntrinsicResolver.Resolve(Type.Literal('hello'), 'Uppercase') + const T = IntrinsicResolve(Type.Literal('hello'), 'Uppercase') Assert.IsTrue(TypeGuard.TLiteral(T)) Assert.IsEqual(T.const, 'HELLO') }) it('Should map literal: Lowercase', () => { - const T = IntrinsicResolver.Resolve(Type.Literal('HELLO'), 'Lowercase') + const T = IntrinsicResolve(Type.Literal('HELLO'), 'Lowercase') Assert.IsTrue(TypeGuard.TLiteral(T)) Assert.IsEqual(T.const, 'hello') }) @@ -64,25 +64,25 @@ describe('type/intrinsic/Map', () => { // Mode: Literal Union // ---------------------------------------------------- it('Should map literal union: Capitalize', () => { - const T = IntrinsicResolver.Resolve(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Capitalize') + const T = IntrinsicResolve(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Capitalize') Assert.IsTrue(TypeGuard.TUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'Hello') Assert.IsEqual(T.anyOf[1].const, 'World') }) it('Should map literal union: Uncapitalize', () => { - const T = IntrinsicResolver.Resolve(Type.Union([Type.Literal('Hello'), Type.Literal('World')]), 'Uncapitalize') + const T = IntrinsicResolve(Type.Union([Type.Literal('Hello'), Type.Literal('World')]), 'Uncapitalize') Assert.IsTrue(TypeGuard.TUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'hello') Assert.IsEqual(T.anyOf[1].const, 'world') }) it('Should map literal union: Uppercase', () => { - const T = IntrinsicResolver.Resolve(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Uppercase') + const T = IntrinsicResolve(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Uppercase') Assert.IsTrue(TypeGuard.TUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'HELLO') Assert.IsEqual(T.anyOf[1].const, 'WORLD') }) it('Should map literal union: Lowercase', () => { - const T = IntrinsicResolver.Resolve(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]), 'Lowercase') + const T = IntrinsicResolve(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]), 'Lowercase') Assert.IsTrue(TypeGuard.TUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'hello') Assert.IsEqual(T.anyOf[1].const, 'world') @@ -91,22 +91,22 @@ describe('type/intrinsic/Map', () => { // Mode: TemplateLiteral // ---------------------------------------------------- it('Should map template literal: Capitalize', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral('hello${1|2}world'), 'Capitalize') + const T = IntrinsicResolve(Type.TemplateLiteral('hello${1|2}world'), 'Capitalize') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(Hello1world|Hello2world)$') }) it('Should map template literal: Uncapitalize', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Uncapitalize') + const T = IntrinsicResolve(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Uncapitalize') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hELLO1WORLD|hELLO2WORLD)$') }) it('Should map template literal: Uppercase', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral('hello${1|2}world'), 'Uppercase') + const T = IntrinsicResolve(Type.TemplateLiteral('hello${1|2}world'), 'Uppercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(HELLO1WORLD|HELLO2WORLD)$') }) it('Should map template literal: Lowercase', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Lowercase') + const T = IntrinsicResolve(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hello1world|hello2world)$') }) @@ -114,22 +114,22 @@ describe('type/intrinsic/Map', () => { // Mode: TemplateLiteral Numeric // ---------------------------------------------------- it('Should map template literal numeric: Capitalize', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Capitalize') + const T = IntrinsicResolve(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Capitalize') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(Hello1|Hello2)$') }) it('Should map template literal numeric: Uncapitalize', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uncapitalize') + const T = IntrinsicResolve(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uncapitalize') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hELLO1|hELLO2)$') }) it('Should map template literal numeric: Uppercase', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uppercase') + const T = IntrinsicResolve(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uppercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(HELLO1|HELLO2)$') }) it('Should map template literal numeric: Lowercase', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Lowercase') + const T = IntrinsicResolve(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hello1|hello2)$') }) @@ -137,27 +137,27 @@ describe('type/intrinsic/Map', () => { // Mode: TemplateLiteral Patterns // ---------------------------------------------------- it('Should map template literal patterns 1', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral('HELLO${string}WORLD'), 'Lowercase') + const T = IntrinsicResolve(Type.TemplateLiteral('HELLO${string}WORLD'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^hello(.*)world$') }) it('Should map template literal patterns 2', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral('HELLO${number}WORLD'), 'Lowercase') + const T = IntrinsicResolve(Type.TemplateLiteral('HELLO${number}WORLD'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^hello(0|[1-9][0-9]*)world$') }) it('Should map template literal patterns 3', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral('${number}${string}'), 'Lowercase') + const T = IntrinsicResolve(Type.TemplateLiteral('${number}${string}'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(0|[1-9][0-9]*)(.*)$') }) it('Should map template literal patterns 3', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral('${number}HELLO${string}'), 'Lowercase') + const T = IntrinsicResolve(Type.TemplateLiteral('${number}HELLO${string}'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(0|[1-9][0-9]*)hello(.*)$') }) it('Should map template literal patterns 3', () => { - const T = IntrinsicResolver.Resolve(Type.TemplateLiteral('${string|number}HELLO'), 'Lowercase') + const T = IntrinsicResolve(Type.TemplateLiteral('${string|number}HELLO'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(stringhello|numberhello)$') })