diff --git a/examples/index.ts b/examples/index.ts index f682e9b9f..8c7fdac6a 100644 --- a/examples/index.ts +++ b/examples/index.ts @@ -19,21 +19,16 @@ import { TOptional, UnionTypeIsOptional, } from '@sinclair/typebox' -import { TObject, TUnion, UnionToTuple, TInteger, TBigInt, TIntersect, TString, TNumber, TBoolean, TNever, TTuple, UnionType, TRecursive } from '@sinclair/typebox' - - -const I = Type.Intersect([ - Type.Intersect([ - Type.Object({ x: Type.String(), y: Type.Literal(1) }), - Type.Object({ x: Type.String(), y: Type.Number() }) - ]), - Type.Intersect([ - Type.Object({ x: Type.String(), y: Type.Number() }), - Type.Object({ x: Type.String(), y: Type.Union([Type.Literal('A'), Type.Literal('B')]) }) - ]) +import { TObject } from '@sinclair/typebox' + +const A = Type.Intersect([Type.String(), Type.String()]) + +const I = Type.Composite([ + Type.Composite([Type.Object({ x: Type.String(), y: Type.Literal(1) }), Type.Object({ x: Type.String(), y: Type.Number() })]), + Type.Composite([Type.Object({ x: Type.Boolean(), y: Type.Number() }), Type.Object({ x: Type.String() })]), ]) -const R = Type.Index(I, ['x', 'y']) +const R = Type.Index(I, ['x']) // prettier-ignore // export type UnionTypeIsOptional = diff --git a/src/typebox.ts b/src/typebox.ts index 329e82b2e..87a91b2cd 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -52,13 +52,12 @@ export type UnionToIntersect = (U extends unknown ? (arg: U) => 0 : never) ex export type UnionLast = UnionToIntersect 0 : never> extends (x: infer L) => 0 ? L : never export type UnionToTuple> = [U] extends [never] ? [] : [...UnionToTuple>, L] export type Discard = T extends [infer L, ...infer R] ? (L extends D ? Discard : [L, ...Discard]) : [] -export type Flat = T extends [] ? [] : T extends [infer L] ? [...Flat] : T extends [infer L, ...infer R] ? [...Flat, ...Flat] : [T] export type Trim = T extends `${' '}${infer U}` ? Trim : T extends `${infer U}${' '}` ? Trim : T export type Assert = T extends E ? T : never export type Evaluate = T extends infer O ? { [K in keyof O]: O[K] } : never export type Ensure = T extends infer U ? U : never // -------------------------------------------------------------------------- -// Type Assertions +// Type Asserts // -------------------------------------------------------------------------- export type AssertProperties = T extends TProperties ? T : TProperties export type AssertRest = T extends E ? T : [] @@ -102,15 +101,17 @@ export type OptionalUnwrapRest = // ------------------------------------------------------------------ // DistinctRest // ------------------------------------------------------------------ +// prettier-ignore export type DistinctRestIncludes = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] + T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? C extends L ? true : DistinctRestIncludes : false +// prettier-ignore export type DistinctRest = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? DistinctRestIncludes extends false + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? DistinctRestIncludes extends false ? DistinctRest : DistinctRest : Acc @@ -127,13 +128,13 @@ export type IntersectTypeIsOptional = // prettier-ignore export type IntersectTypeOptional> = IntersectTypeIsOptional extends true - ? TOptional>> - : TIntersect> + ? TOptional> + : TIntersect // prettier-ignore -export type IntersectType = - T extends [] ? TNever : - T extends [TSchema] ? AssertType : - IntersectTypeOptional +export type IntersectType = //extends TSchema[] = DistinctRest> = + D extends [] ? TNever : + D extends [TSchema] ? T[0] : + IntersectTypeOptional // -------------------------------------------------------------------------- // UnionType // -------------------------------------------------------------------------- @@ -147,13 +148,13 @@ export type UnionTypeIsOptional = // prettier-ignore export type UnionTypeOptional> = UnionTypeIsOptional extends true - ? TOptional>> - : TUnion> + ? TOptional> + : TUnion // prettier-ignore -export type UnionType = - T extends [] ? TNever : - T extends [TSchema] ? T[0] : - UnionTypeOptional +export type UnionType = // = DistinctRest> = + D extends [] ? TNever : + D extends [TSchema] ? T[0] : + UnionTypeOptional // -------------------------------------------------------------------------- // TSchema // -------------------------------------------------------------------------- @@ -560,10 +561,9 @@ export type TUnevaluatedProperties = undefined | TSchema | boolean export interface IntersectOptions extends SchemaOptions { unevaluatedProperties?: TUnevaluatedProperties } -export type TIntersectResolve = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? Static & TIntersectResolve : {} export interface TIntersect extends TSchema, IntersectOptions { [Kind]: 'Intersect' - static: TIntersectResolve + static: TupleToIntersect<{ [K in keyof T]: Static, this['params']> }> type?: 'object' allOf: [...T] } @@ -2422,6 +2422,24 @@ export namespace TypeClone { return { ...Visit(schema), ...options } } } + +// -------------------------------------------------------------------------- +// DistinctRest +// -------------------------------------------------------------------------- +export namespace DistinctRest { + function DistinctIncludes(schemas: TSchema[], candidate: TSchema) { + return schemas.find((schema) => TypeExtends.Extends(candidate, schema) === TypeExtendsResult.True) !== undefined + } + // prettier-ignore + export function Distinct(schemas: [...T]): DistinctRest { + return schemas.reduce((acc, candidate) => { + return !DistinctIncludes(acc, candidate) + ? [...acc, candidate] + : [...acc] + }, [] as TSchema[]) as DistinctRest + } +} + // -------------------------------------------------------------------------- // IndexedAccessor // -------------------------------------------------------------------------- diff --git a/test/static/indexed.ts b/test/static/indexed.ts index a33af5130..16600b379 100644 --- a/test/static/indexed.ts +++ b/test/static/indexed.ts @@ -191,6 +191,11 @@ import { Type, Static } from '@sinclair/typebox' const D = Type.Object({ x: Type.String() }) const I = Type.Intersect([Type.Union([A, B]), Type.Intersect([C, D])]) const R = Type.Index(I, ['x', 'y']) + + // + // TUnion<[TIntersect<[TString, TNumber]>, TIntersect<[TLiteral<1>, TNumber]>]> + // TUnion<[TIntersect<[TString, TNumber, TString, TString]>, TIntersect<[TLiteral<1>, TNumber, TNumber]>]> + // TUnion<[TIntersect<[TUnion<[TString, TNumber]>, TIntersect<[TString, TString]>]>, TIntersect<[TUnion<[TLiteral<1>, TNumber]>, TNumber]>]> type O = Static Expect(R).ToStatic() }