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 23, 2023
1 parent 611e8ac commit 538a99f
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 34 deletions.
22 changes: 17 additions & 5 deletions examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,28 @@ import {
TLiteralValue,
IsTemplateLiteralFiniteCheck,
UnionToTuple,
PatternNumber
PatternNumber,
Accessor,
Indexer,
} from '@sinclair/typebox'

const T = Type.Union([
Type.Object({ x: Type.Number() }),
Type.Object({ y: Type.Number() })
Type.Object({ x: Type.Number() })
])
const I = Type.Index(T, ['x'])

console.log(I)
const I = Type.Index(T, Type.Literal('x'))

type T = { x: 1 } | { y: 1 }
type C = Accessor.Union<typeof T['anyOf'], 'x'>
type Z = C['anyOf'][1]

type P = Z extends TNever ? 1 : 2

type N = Accessor.NeverCheck<C['anyOf']>

const C = Accessor.Union(T.anyOf, 'x')



console.log(C)

115 changes: 87 additions & 28 deletions src/typebox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,12 @@ export const PatternStringExact = `^${PatternString}$`
// --------------------------------------------------------------------------
// Into
// --------------------------------------------------------------------------
export function Into<T>(func: () => T): T {
export function Into<U>(func: () => U): U {
return func()
}
export function Infer<T, U>(T: T, func: (T: T) => U): U {
return func(T)
}
// --------------------------------------------------------------------------
// Helpers
// --------------------------------------------------------------------------
Expand Down Expand Up @@ -2625,7 +2628,9 @@ export namespace IndexerOld {
// ------------------------------------------------------------------
// prettier-ignore
export namespace Indexer {
// ----------------------------------------------------------------
// TemplateLiteral
// ----------------------------------------------------------------
export type TemplateLiteral<T extends TTemplateLiteral, F = IsTemplateLiteralFiniteCheck<T>> = (
F extends true
? UnionToTuple<Static<T>>
Expand All @@ -2641,7 +2646,9 @@ export namespace Indexer {
: []
: []
}
// ----------------------------------------------------------------
// Union
// ----------------------------------------------------------------
export type Union<T extends TSchema[]> =
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? [...Resolve<L>, ...Union<R>]
Expand All @@ -2652,15 +2659,19 @@ export namespace Indexer {
? [...Resolve(L), ...Union(R)]
: []
}
// ----------------------------------------------------------------
// Literal
// ----------------------------------------------------------------
export type Literal<T extends TLiteralValue> =
T extends PropertyKey
? [`${T}`]
: []
export function TLiteral(schema: TLiteral): string[] {
return [schema.const.toString()]
}
// ----------------------------------------------------------------
// Resolve
// ----------------------------------------------------------------
export type Resolve<T extends TSchema> =
T extends TTemplateLiteral ? TemplateLiteral<T> :
T extends TUnion<infer S> ? Union<S> :
Expand All @@ -2678,7 +2689,9 @@ export namespace Indexer {
[]
))]
}
// ----------------------------------------------------------------
// ResolvePattern
// ----------------------------------------------------------------
export function ResolvePattern(schema: TSchema): string {
const keys = Resolve(schema)
const pattern = keys.map((key) => key === 'number' ? PatternNumber : key)
Expand All @@ -2690,6 +2703,74 @@ export namespace Indexer {
// ------------------------------------------------------------------
// prettier-ignore
export namespace Accessor {
// ----------------------------------------------------------------
// NeverCheck
// ----------------------------------------------------------------
export type NeverCheck<T extends TSchema[]> =
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? L extends TNever
? true
: NeverCheck<R>
: false
export function NeverCheck(T: TSchema[]): boolean {
const [L, ...R] = T
return T.length > 0
? TypeGuard.TNever(L)
? true
: NeverCheck(R)
: false
}
// ----------------------------------------------------------------
// Intersect
// ----------------------------------------------------------------
export type IntersectCollect<T extends TSchema[], K extends PropertyKey> =
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? Key<L, K> extends infer N extends TSchema
? N extends TNever
? [...IntersectCollect<R, K>]
: [N, ...IntersectCollect<R, K>]
: []
: []
function IntersectCollect(T: TSchema[], K: PropertyKey): TSchema[] {
const [L, ...R] = T
return T.length > 0
? Infer(Key(L, K), N => {
return TypeGuard.TNever(N)
? [...IntersectCollect(R, K)]
: [N, ...IntersectCollect(R, K)]
})
: []
}
export type Intersect<T extends TSchema[], K extends PropertyKey, C extends TSchema[] = IntersectCollect<T, K>> =
IntersectType.Resolve<C>
export function Intersect(T: TSchema[], K: PropertyKey): TSchema {
const C = IntersectCollect(T, K)
return IntersectType.Resolve(C)
}
// ----------------------------------------------------------------
// Union
// ----------------------------------------------------------------
export type UnionCollect<T extends TSchema[], K extends PropertyKey> =
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? [Key<L, K>, ...UnionCollect<R, K>]
: []
export type Union<T extends TSchema[], K extends PropertyKey, C extends TSchema[] = UnionCollect<T, K>, N extends boolean = NeverCheck<C>> =
N extends true
? TNever
: TUnion<C>
export function UnionCollect(T: TSchema[], K: PropertyKey): TSchema[] {
const [L, ...R] = T
return T.length > 0
? [Key(L, K), ...UnionCollect(R, K)]
: []
}
export function Union(T: TSchema[], K: PropertyKey): TSchema {
const C = UnionCollect(T, K)
const N = NeverCheck(C)
return N === true
? Type.Never()
: UnionType.Resolve(C)
}
// ----------------------------------------------------------------
// Property
// ----------------------------------------------------------------
Expand Down Expand Up @@ -2730,43 +2811,21 @@ export namespace Accessor {
: Type.Never()
}
// ----------------------------------------------------------------
// Rest
// ----------------------------------------------------------------
export type Rest<T extends TSchema[], K extends PropertyKey> =
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? Key<L, K> extends infer N extends TSchema
? N extends TNever
? [...Rest<R, K>]
: [N, ...Rest<R, K>]
: []
: []
function Rest(T: TSchema[], K: PropertyKey): TSchema[] {
const [L, ...R] = T
return T.length > 0
? Into(() => {
const N = Key(L, K)
return TypeGuard.TNever(N)
? [...Rest(R, K)]
: [N, ...Rest(R, K)]
})
: []
}
// ----------------------------------------------------------------
// Key
// ----------------------------------------------------------------
export type Key<T extends TSchema, K extends PropertyKey> = (
T extends TRecursive<infer S> ? Key<S, K> :
T extends TIntersect<infer S> ? IntersectType.Resolve<Rest<S, K>> :
T extends TUnion<infer S> ? UnionType.Resolve<Rest<S, K>> :
T extends TIntersect<infer S> ? Intersect<S, K> :
T extends TUnion<infer S> ? Union<S, K> :
T extends TTuple<infer S> ? Tuple<S, K> :
T extends TArray<infer S> ? Array<S, K> :
T extends TObject<infer S> ? Property<S, K> :
TNever
)
export function Key(T: TSchema, K: PropertyKey) {
return (
TypeGuard.TIntersect(T) ? IntersectType.Resolve(Rest(T.allOf, K)) :
TypeGuard.TUnion(T) ? UnionType.Resolve(Rest(T.anyOf, K)) :
TypeGuard.TIntersect(T) ? Intersect(T.allOf, K) :
TypeGuard.TUnion(T) ? Union(T.anyOf, K) :
TypeGuard.TTuple(T) ? Tuple(T.items ?? [], K) :
TypeGuard.TArray(T) ? Array(T.items, K) :
TypeGuard.TObject(T) ? Property(T.properties, K) :
Expand Down Expand Up @@ -3267,7 +3326,7 @@ export class JsonTypeBuilder extends TypeBuilder {
) as TExtract<L, R>
}
/** `[Json]` Returns an Indexed property type for the given keys */
public Index<T extends TSchema, I extends TSchema, K extends PropertyKey[] = TIndexer<I>>(T: T, keys: I, options?: SchemaOptions): Accessor.Resolve<T, K>
public Index<T extends TSchema, I extends TSchema, K extends PropertyKey[] = Indexer.Resolve<I>>(T: T, keys: I, options?: SchemaOptions): Accessor.Resolve<T, K>
/** `[Json]` Returns an Indexed property type for the given keys */
public Index<T extends TSchema, K extends PropertyKey[]>(T: T, K: [...K], options?: SchemaOptions): Accessor.Resolve<T, K>
/** `[Json]` Returns an Indexed property type for the given keys */
Expand Down
2 changes: 1 addition & 1 deletion test/static/indexed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ import { Type, Static } from '@sinclair/typebox'
}
{
const T = Type.Object({
0: Type.Number(),
'0': Type.Number(),
'1': Type.String(),
})
const R = Type.Index(T, Type.KeyOf(T))
Expand Down

0 comments on commit 538a99f

Please sign in to comment.