Skip to content

Commit

Permalink
Reimplement Index Types
Browse files Browse the repository at this point in the history
  • Loading branch information
sinclairzx81 committed Nov 22, 2023
1 parent 507583e commit 1e92672
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 32 deletions.
21 changes: 8 additions & 13 deletions examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T extends TSchema[]> =
Expand Down
56 changes: 37 additions & 19 deletions src/typebox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,12 @@ export type UnionToIntersect<U> = (U extends unknown ? (arg: U) => 0 : never) ex
export type UnionLast<U> = UnionToIntersect<U extends unknown ? (x: U) => 0 : never> extends (x: infer L) => 0 ? L : never
export type UnionToTuple<U, L = UnionLast<U>> = [U] extends [never] ? [] : [...UnionToTuple<Exclude<U, L>>, L]
export type Discard<T extends unknown[], D extends unknown> = T extends [infer L, ...infer R] ? (L extends D ? Discard<R, D> : [L, ...Discard<R, D>]) : []
export type Flat<T> = T extends [] ? [] : T extends [infer L] ? [...Flat<L>] : T extends [infer L, ...infer R] ? [...Flat<L>, ...Flat<R>] : [T]
export type Trim<T> = T extends `${' '}${infer U}` ? Trim<U> : T extends `${infer U}${' '}` ? Trim<U> : T
export type Assert<T, E> = T extends E ? T : never
export type Evaluate<T> = T extends infer O ? { [K in keyof O]: O[K] } : never
export type Ensure<T> = T extends infer U ? U : never
// --------------------------------------------------------------------------
// Type Assertions
// Type Asserts
// --------------------------------------------------------------------------
export type AssertProperties<T> = T extends TProperties ? T : TProperties
export type AssertRest<T, E extends TSchema[] = TSchema[]> = T extends E ? T : []
Expand Down Expand Up @@ -102,15 +101,17 @@ export type OptionalUnwrapRest<T extends TSchema[]> =
// ------------------------------------------------------------------
// DistinctRest
// ------------------------------------------------------------------
// prettier-ignore
export type DistinctRestIncludes<T extends TSchema[], C extends TSchema> =
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<R, C>
: false
// prettier-ignore
export type DistinctRest<T extends TSchema[], Acc extends TSchema[] = []> =
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? DistinctRestIncludes<Acc, L> extends false
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? DistinctRestIncludes<Acc, L> extends false
? DistinctRest<R, [...Acc, L]>
: DistinctRest<R, [...Acc]>
: Acc
Expand All @@ -127,13 +128,13 @@ export type IntersectTypeIsOptional<T extends TSchema[]> =
// prettier-ignore
export type IntersectTypeOptional<T extends TSchema[], U extends TSchema[] = OptionalUnwrapRest<T>> =
IntersectTypeIsOptional<T> extends true
? TOptional<TIntersect<DistinctRest<U>>>
: TIntersect<DistinctRest<U>>
? TOptional<TIntersect<U>>
: TIntersect<U>
// prettier-ignore
export type IntersectType<T extends TSchema[]> =
T extends [] ? TNever :
T extends [TSchema] ? AssertType<T[0]> :
IntersectTypeOptional<T>
export type IntersectType<T extends TSchema[], D extends TSchema[] = T> = //extends TSchema[] = DistinctRest<T>> =
D extends [] ? TNever :
D extends [TSchema] ? T[0] :
IntersectTypeOptional<D>
// --------------------------------------------------------------------------
// UnionType
// --------------------------------------------------------------------------
Expand All @@ -147,13 +148,13 @@ export type UnionTypeIsOptional<T extends TSchema[]> =
// prettier-ignore
export type UnionTypeOptional<T extends TSchema[], U extends TSchema[] = OptionalUnwrapRest<T>> =
UnionTypeIsOptional<T> extends true
? TOptional<TUnion<DistinctRest<U>>>
: TUnion<DistinctRest<U>>
? TOptional<TUnion<U>>
: TUnion<U>
// prettier-ignore
export type UnionType<T extends TSchema[]> =
T extends [] ? TNever :
T extends [TSchema] ? T[0] :
UnionTypeOptional<T>
export type UnionType<T extends TSchema[], D extends TSchema[] = T> = // = DistinctRest<T>> =
D extends [] ? TNever :
D extends [TSchema] ? T[0] :
UnionTypeOptional<D>
// --------------------------------------------------------------------------
// TSchema
// --------------------------------------------------------------------------
Expand Down Expand Up @@ -560,10 +561,9 @@ export type TUnevaluatedProperties = undefined | TSchema | boolean
export interface IntersectOptions extends SchemaOptions {
unevaluatedProperties?: TUnevaluatedProperties
}
export type TIntersectResolve<T extends TSchema[], P extends unknown[]> = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? Static<L, P> & TIntersectResolve<R, P> : {}
export interface TIntersect<T extends TSchema[] = TSchema[]> extends TSchema, IntersectOptions {
[Kind]: 'Intersect'
static: TIntersectResolve<T, this['params']>
static: TupleToIntersect<{ [K in keyof T]: Static<AssertType<T[K]>, this['params']> }>
type?: 'object'
allOf: [...T]
}
Expand Down Expand Up @@ -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<T extends TSchema[]>(schemas: [...T]): DistinctRest<T> {
return schemas.reduce((acc, candidate) => {
return !DistinctIncludes(acc, candidate)
? [...acc, candidate]
: [...acc]
}, [] as TSchema[]) as DistinctRest<T>
}
}

// --------------------------------------------------------------------------
// IndexedAccessor
// --------------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions test/static/indexed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof R>
Expect(R).ToStatic<number | string>()

Check failure on line 200 in test/static/indexed.ts

View workflow job for this annotation

GitHub Actions / TypeBox (16.x, ubuntu-latest)

Type 'string | number' does not satisfy the constraint '1'.

Check failure on line 200 in test/static/indexed.ts

View workflow job for this annotation

GitHub Actions / TypeBox (16.x, macOS-latest)

Type 'string | number' does not satisfy the constraint '1'.

Check failure on line 200 in test/static/indexed.ts

View workflow job for this annotation

GitHub Actions / TypeBox (18.x, ubuntu-latest)

Type 'string | number' does not satisfy the constraint '1'.

Check failure on line 200 in test/static/indexed.ts

View workflow job for this annotation

GitHub Actions / TypeBox (20.x, ubuntu-latest)

Type 'string | number' does not satisfy the constraint '1'.
}
Expand Down

0 comments on commit 1e92672

Please sign in to comment.