diff --git a/examples/index.ts b/examples/index.ts index 0111b7a0..6dd4cfc4 100644 --- a/examples/index.ts +++ b/examples/index.ts @@ -51,103 +51,140 @@ import { // prettier-ignore export namespace KeyResolver { // ---------------------------------------------------------------- - // Intersect + // Collect // ---------------------------------------------------------------- - export type Intersect = ( + export type Collect = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? [...Resolve, ...Intersect] + ? [Resolve, ...Collect] : [] ) - export function Intersect(T: [...T]): Intersect { + export function Collect(T: [...T]): Collect { const [L, ...R] = T return ( T.length > 0 - ? [...Resolve(L), ...Intersect(R)] + ? [Resolve(L), ...Collect(R)] : [] - ) as Intersect + ) as Collect } // ---------------------------------------------------------------- - // UnionKeyIn + // Includes // ---------------------------------------------------------------- - export type UnionKeyIn = ( + export type Includes = ( T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] ? S extends L ? true - : UnionKeyIn + : Includes : false ) - export function UnionKeyIn(T: [...T], S: S) { + export function Includes(T: [...T], S: S) { return ( T.includes(S) - ) as UnionKeyIn + ) as Includes } // ---------------------------------------------------------------- - // UnionKeysIn + // IntersectDistinct // ---------------------------------------------------------------- - export type UnionKeysIn = ( + export type IntersectDistinct = ( T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? UnionKeyIn extends true - ? [L, ...UnionKeysIn] - : [...UnionKeysIn] - : [] + ? Includes extends true + ? [...IntersectDistinct] + : [L, ...IntersectDistinct] + : S ) - export function UnionKeysIn(T: [...T], S: [...S]): UnionKeysIn { + export function IntersectDistinct(T: [...T], S: [...S]): IntersectDistinct { const [L, ...R] = T return ( T.length > 0 - ? UnionKeyIn(S, L) === true - ? [L, ...UnionKeysIn(R, S)] - : [...UnionKeysIn(R, S)] - : [] - ) as UnionKeysIn + ? Includes(S, L) === true + ? [...IntersectDistinct(R, S)] + : [L, ...IntersectDistinct(R, S)] + : S + ) as IntersectDistinct } // ---------------------------------------------------------------- - // UnionKeys + // IntersectIntersection // ---------------------------------------------------------------- - export type UnionKeys = ( + export type IntersectIntersection = ( T extends [infer L extends PropertyKey[]] ? L : T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] - ? UnionKeysIn> + ? IntersectDistinct> : [] ) - export function UnionKeys(T: [...T]): UnionKeys { + export function IntersectIntersection(T: [...T]): IntersectIntersection { return ( T.length === 1 ? T[0] : Into(() => { const [L, ...R] = T return L.length > 0 - ? UnionKeysIn(L, UnionKeys(R)) + ? IntersectDistinct(L, IntersectIntersection(R)) : [] }) - ) as UnionKeys + ) as IntersectIntersection } // ---------------------------------------------------------------- - // UnionCollect + // Intersect // ---------------------------------------------------------------- - export type UnionCollect = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? [Resolve, ...UnionCollect] + export type Intersect> = ( + IntersectIntersection + ) + export function Intersect(T: [...T], C = Collect(T)): Intersect { + return ( + IntersectIntersection(C as PropertyKey[][]) as Intersect + ) + } + // ---------------------------------------------------------------- + // UnionDistinct + // ---------------------------------------------------------------- + export type UnionDistinct = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? Includes extends true + ? [L, ...UnionDistinct] + : [...UnionDistinct] : [] ) - export function UnionCollect(T: [...T]): UnionCollect { + export function UnionDistinct(T: [...T], S: [...S]): UnionDistinct { const [L, ...R] = T return ( T.length > 0 - ? [Resolve(L), ...UnionCollect(R)] + ? Includes(S, L) === true + ? [L, ...UnionDistinct(R, S)] + : [...UnionDistinct(R, S)] + : [] + ) as UnionDistinct + } + // ---------------------------------------------------------------- + // UnionIntersection + // ---------------------------------------------------------------- + export type UnionIntersection = ( + T extends [infer L extends PropertyKey[]] + ? L + : T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] + ? UnionDistinct> : [] - ) as UnionCollect + ) + export function UnionIntersection(T: [...T]): UnionIntersection { + return ( + T.length === 1 + ? T[0] + : Into(() => { + const [L, ...R] = T + return L.length > 0 + ? UnionDistinct(L, UnionIntersection(R)) + : [] + }) + ) as UnionIntersection } // ---------------------------------------------------------------- // Union // ---------------------------------------------------------------- - export type Union> = ( - UnionKeys + export type Union> = ( + UnionIntersection ) - export function Union(T: [...T], C = UnionCollect(T)): Union { + export function Union(T: [...T], C = Collect(T)): Union { return ( - UnionKeys(C as PropertyKey[][]) as Union + UnionIntersection(C as PropertyKey[][]) as Union ) } // ---------------------------------------------------------------- @@ -240,25 +277,26 @@ export namespace KeyResolver { } } -const A = KeyResolver.ResolvePattern(Type.Intersect([ - Type.Record(Type.Number(), Type.Number()), +const A = KeyResolver.Intersect([ Type.Object({ x: Type.Number(), y: Type.Number(), - z: Type.Number() - }) -])) + z: Type.Number(), + }), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), -const B = KeyResolver.Union([ - Type.Object({ x: Type.Number(), y: Type.Number() }), - Type.Object({ x: Type.Number(), y: Type.Number() }) + Type.Record(Type.Number(), Type.Number()), ]) console.log(A) +// const B = KeyResolver.Resolve(Type.Union([ +// Type.Object({ x: Type.Number(), y: Type.Number() }), +// Type.Object({ x: Type.Number(), z: Type.Number() }), +// ])) - - - - - +// console.log(B) diff --git a/src/typebox.ts b/src/typebox.ts index 0e426873..e8e37db0 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -2612,147 +2612,143 @@ export namespace Increment { // ---------------------------------------------------------------- // KeyResolver // ---------------------------------------------------------------- -// export interface KeyResolverOptions { -// includePatterns: boolean -// } -// // prettier-ignore -// export namespace KeyResolver { -// function UnwrapPattern(key: string) { -// return key[0] === '^' && key[key.length - 1] === '$' ? key.slice(1, key.length - 1) : key -// } -// function TIntersect(schema: TIntersect, options: KeyResolverOptions): string[] { -// return schema.allOf.reduce((acc, schema) => [...acc, ...Visit(schema, options)], [] as string[]) -// } -// function TUnion(schema: TUnion, options: KeyResolverOptions): string[] { -// const sets = schema.anyOf.map((inner) => Visit(inner, options)) -// return [...sets.reduce((set, outer) => outer.map((key) => (sets.every((inner) => inner.includes(key)) ? set.add(key) : set))[0], new Set())] -// } -// function TObject(schema: TObject, options: KeyResolverOptions): string[] { -// return Object.getOwnPropertyNames(schema.properties) -// } -// function TRecord(schema: TRecord, options: KeyResolverOptions): string[] { -// return options.includePatterns ? Object.getOwnPropertyNames(schema.patternProperties) : [] -// } -// function Visit(schema: TSchema, options: KeyResolverOptions): string[] { -// return ( -// TypeGuard.TIntersect(schema) ? TIntersect(schema, options) : -// TypeGuard.TUnion(schema) ? TUnion(schema, options) : -// TypeGuard.TObject(schema) ? TObject(schema, options) : -// TypeGuard.TRecord(schema) ? TRecord(schema, options) : -// [] -// ) -// } -// /** Resolves a regular expression pattern matching all keys in this schema */ -// export function ResolvePattern(schema: TSchema): string { -// const keys = Resolve(schema, { includePatterns: true }) -// const pattern = keys.map((key) => `(${UnwrapPattern(key)})`) -// return `^(${pattern.join('|')})$` -// } -// /** Resolves an array of keys derived from this schema */ -// export function Resolve(schema: TSchema, options: KeyResolverOptions): string[] { -// return [...new Set(Visit(schema, options))] -// } -// } // prettier-ignore export namespace KeyResolver { // ---------------------------------------------------------------- - // Intersect + // Collect // ---------------------------------------------------------------- - export type Intersect = ( + export type Collect = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? [...Resolve, ...Intersect] + ? [Resolve, ...Collect] : [] ) - export function Intersect(T: [...T]): Intersect { + export function Collect(T: [...T]): Collect { const [L, ...R] = T return ( T.length > 0 - ? [...Resolve(L), ...Intersect(R)] + ? [Resolve(L), ...Collect(R)] : [] - ) as Intersect + ) as Collect } // ---------------------------------------------------------------- - // UnionKeyIn + // Includes // ---------------------------------------------------------------- - export type UnionKeyIn = ( + export type Includes = ( T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] ? S extends L ? true - : UnionKeyIn + : Includes : false ) - export function UnionKeyIn(T: [...T], S: S) { + export function Includes(T: [...T], S: S) { return ( T.includes(S) - ) as UnionKeyIn + ) as Includes } // ---------------------------------------------------------------- - // UnionKeysIn + // IntersectDistinct // ---------------------------------------------------------------- - export type UnionKeysIn = ( + export type IntersectDistinct = ( T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? UnionKeyIn extends true - ? [L, ...UnionKeysIn] - : [...UnionKeysIn] - : [] + ? Includes extends true + ? [...IntersectDistinct] + : [L, ...IntersectDistinct] + : S ) - export function UnionKeysIn(T: [...T], S: [...S]): UnionKeysIn { + export function IntersectDistinct(T: [...T], S: [...S]): IntersectDistinct { const [L, ...R] = T return ( T.length > 0 - ? UnionKeyIn(S, L) === true - ? [L, ...UnionKeysIn(R, S)] - : [...UnionKeysIn(R, S)] - : [] - ) as UnionKeysIn + ? Includes(S, L) === true + ? [...IntersectDistinct(R, S)] + : [L, ...IntersectDistinct(R, S)] + : S + ) as IntersectDistinct } // ---------------------------------------------------------------- - // UnionKeys + // IntersectIntersection // ---------------------------------------------------------------- - export type UnionKeys = ( + export type IntersectIntersection = ( T extends [infer L extends PropertyKey[]] ? L : T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] - ? UnionKeysIn> + ? IntersectDistinct> : [] ) - export function UnionKeys(T: [...T]): UnionKeys { + export function IntersectIntersection(T: [...T]): IntersectIntersection { return ( T.length === 1 ? T[0] : Into(() => { const [L, ...R] = T return L.length > 0 - ? UnionKeysIn(L, UnionKeys(R)) + ? IntersectDistinct(L, IntersectIntersection(R)) : [] }) - ) as UnionKeys + ) as IntersectIntersection + } + // ---------------------------------------------------------------- + // Intersect + // ---------------------------------------------------------------- + export type Intersect> = ( + IntersectIntersection + ) + export function Intersect(T: [...T], C = Collect(T)): Intersect { + return ( + IntersectIntersection(C as PropertyKey[][]) as Intersect + ) } // ---------------------------------------------------------------- - // UnionCollect + // UnionDistinct // ---------------------------------------------------------------- - export type UnionCollect = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? [Resolve, ...UnionCollect] + export type UnionDistinct = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? Includes extends true + ? [L, ...UnionDistinct] + : [...UnionDistinct] : [] ) - export function UnionCollect(T: [...T]): UnionCollect { + export function UnionDistinct(T: [...T], S: [...S]): UnionDistinct { const [L, ...R] = T return ( T.length > 0 - ? [Resolve(L), ...UnionCollect(R)] + ? Includes(S, L) === true + ? [L, ...UnionDistinct(R, S)] + : [...UnionDistinct(R, S)] : [] - ) as UnionCollect + ) as UnionDistinct + } + // ---------------------------------------------------------------- + // UnionIntersection + // ---------------------------------------------------------------- + export type UnionIntersection = ( + T extends [infer L extends PropertyKey[]] + ? L + : T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] + ? UnionDistinct> + : [] + ) + export function UnionIntersection(T: [...T]): UnionIntersection { + return ( + T.length === 1 + ? T[0] + : Into(() => { + const [L, ...R] = T + return L.length > 0 + ? UnionDistinct(L, UnionIntersection(R)) + : [] + }) + ) as UnionIntersection } // ---------------------------------------------------------------- // Union // ---------------------------------------------------------------- - export type Union> = ( - UnionKeys + export type Union> = ( + UnionIntersection ) - export function Union(T: [...T], C = UnionCollect(T)): Union { + export function Union(T: [...T], C = Collect(T)): Union { return ( - UnionKeys(C as PropertyKey[][]) as Union + UnionIntersection(C as PropertyKey[][]) as Union ) } // ----------------------------------------------------------------