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 2b5fa1c commit f8935c4
Show file tree
Hide file tree
Showing 7 changed files with 480 additions and 234 deletions.
204 changes: 85 additions & 119 deletions examples/index.ts
Original file line number Diff line number Diff line change
@@ -1,141 +1,107 @@
import { TypeSystem } from '@sinclair/typebox/system'
import { TypeCompiler } from '@sinclair/typebox/compiler'
import { Value, ValuePointer } from '@sinclair/typebox/value'
import { Type, Accessor, TemplateLiteralParser, TemplateLiteralFinite, TemplateLiteralResolver, TypeGuard, TIntersect, TUnion, TTemplateLiteral, IsTemplateLiteralFiniteCheck, UnionToTuple, Static, TSchema, TLiteralValue, TLiteral, TNumber, TInteger, TBigInt, TString, IndexerOptions, PatternNumber } from '@sinclair/typebox'
import { TObject } from '@sinclair/typebox'

// function TTemplateLiteral(schema: TTemplateLiteral, options: IndexerOptions): string[] {
// const expression = TemplateLiteralParser.ParseExact(schema.pattern)
// const finite = TemplateLiteralFinite.Check(expression)
// if (!finite && options.includePatterns) return [schema.pattern]
// if (!finite && !options.includePatterns) return []
// const resolved = TemplateLiteralResolver.Resolve(schema)
// return TypeGuard.TUnionLiteral(resolved) ? resolved.anyOf.map((schema) => schema.const.toString()) : []
// }

// --------------------------------------------------------------------------
// TIndexer
// Accessor
// --------------------------------------------------------------------------
export type TIndexerTemplateLiteral<T extends TTemplateLiteral, F = IsTemplateLiteralFiniteCheck<T>> = F extends true ? UnionToTuple<Static<T>> : []
export type TIndexerUnion<T extends TSchema[]> = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [...TIndexer<L>, ...TIndexerUnion<R>] : []
export type TIndexerLiteral<T extends TLiteralValue> = T extends PropertyKey ? [T] : []
// prettier-ignore
export type TIndexer<T extends TSchema> =
T extends TTemplateLiteral ? TIndexerTemplateLiteral<T> :
T extends TUnion<infer S> ? TIndexerUnion<S> :
T extends TLiteral<infer S> ? TIndexerLiteral<S> :
T extends TNumber ? ['number'] : // tuple + array indexing
T extends TInteger ? ['number'] : // tuple + array indexing
[]

export namespace Indexer {
function TTemplateLiteral(schema: TTemplateLiteral): string[] {
const expression = TemplateLiteralParser.ParseExact(schema.pattern)
const finite = TemplateLiteralFinite.Check(expression)
if (!finite) return []
const resolved = TemplateLiteralResolver.Resolve(schema)
return TypeGuard.TUnionLiteral(resolved) ? resolved.anyOf.map((schema) => schema.const.toString()) : []
}
function Union(schema: TSchema[]): string[] {
const [L, ...R] = schema
return (schema.length > 0) ? [...Keys(L), ...Union(R)] : []
}
function TLiteral(schema: TLiteral): string[] {
return [schema.const.toString()]
}
function Visit(schema: TSchema) {
return (
TypeGuard.TTemplateLiteral(schema) ? TTemplateLiteral(schema) :
TypeGuard.TUnion(schema) ? Union(schema.anyOf) :
TypeGuard.TLiteral(schema) ? TLiteral(schema) :
TypeGuard.TNumber(schema) ? ['number'] : // tuple + array indexing
TypeGuard.TInteger(schema) ? ['number'] : // tuple + array indexing
[]
)
}
export function Pattern(schema: TSchema) {
const keys = Keys(schema)
const pattern = keys.map((key) => key === 'number' ? PatternNumber : key)
return `^(${pattern.join('|')})$`
}
export function Keys(schema: TSchema) {
return [...new Set(Visit(schema))]
}
}
import { Type, Optional, TOptional, TReadonly, TObject, TTuple, TIntersect, TUnion, Kind, TSchema, TypeClone, TypeGuard, ValueGuard, TPropertyKey, SchemaOptions, TProperties, TNever, TRecursive, TArray, Discard, TNumber, TString, TBoolean } from "@sinclair/typebox"

const K = Indexer.Pattern(Type.Union([
Type.String(),
Type.Number(),
Type.String()
]))
export function Into<T>(func: () => T): T {
return func()
}

const T = Type.Object({
x: Type.String(),
y: Type.Union([
Type.Number(),
Type.BigInt()
])
})

const I = Type.Index(T, Type.KeyOf(T))

console.log(K)

// export interface IndexerOptions {
// includePatterns: boolean
// }
// ------------------------------------------------------------------
// Accessor
// ------------------------------------------------------------------
// export type TAccessorProperty<T extends TProperties, K extends PropertyKey> = K extends keyof T ? T[K] : TNever
// export type TAccessorTuple<T extends TSchema[], K extends PropertyKey> = K extends keyof T ? T[K] : K extends 'number' ? UnionTypeResolve<T> : TNever
// export type TAccessorArray<T extends TSchema, K extends PropertyKey> = K extends number ? T : TNever
// // prettier-ignore
// export namespace Indexer {
// function UnwrapPattern(key: string) {
// return key[0] === '^' && key[key.length - 1] === '$' ? key.slice(1, key.length - 1) : key
// }
// export type TAccessorRest<T extends TSchema[], K extends PropertyKey> =
// T extends [infer L extends TSchema, ...infer R extends TSchema[]]
// ? TAccessorKey<L, K> extends infer N extends TSchema
// ? N extends TNever
// ? [...TAccessorRest<R, K>]
// : [N, ...TAccessorRest<R, K>]
// : []
// : []
// // prettier-ignore
// export type TAccessorKey<T extends TSchema, K extends PropertyKey> =
// T extends TRecursive<infer S> ? TAccessorKey<S, K> :
// T extends TIntersect<infer S> ? IntersectTypeResolve<TAccessorRest<S, K>> :
// T extends TUnion<infer S> ? UnionTypeResolve<TAccessorRest<S, K>> :
// T extends TTuple<infer S> ? TAccessorTuple<S, K> :
// T extends TArray<infer S> ? TAccessorArray<S, K> :
// T extends TObject<infer S> ? TAccessorProperty<S, K> :
// TNever
// // prettier-ignore
// export type TAccessorKeys<T extends TSchema, K extends PropertyKey[]> =
// K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
// ? [TAccessorKey<T, L>, ...TAccessorKeys<T, R>]
// : []

// function TIntersect(schema: TIntersect, options: IndexerOptions): string[] {
// return schema.allOf.reduce((acc, schema) => [...acc, ...Visit(schema, options)], [] as string[])
// }
// function TUnion(schema: TUnion, options: IndexerOptions): string[] {
// const sets = schema.anyOf.reduce((acc, schema) => [...acc, ...Visit(schema, options)], [] as string[])
// console.log('Accessor Union', sets)
// return [...sets.reduce((set, outer) => outer.map((key) => (sets.every((inner) => inner.includes(key)) ? set.add(key) : set))[0], new Set<string>())]
// }
// function TObject(schema: TObject, options: IndexerOptions): string[] {
// return Object.getOwnPropertyNames(schema.properties)
// }
// function TRecord(schema: TRecord, options: IndexerOptions): string[] {
// return options.includePatterns ? Object.getOwnPropertyNames(schema.patternProperties) : []
// export type TAccessor<T extends TSchema, K extends PropertyKey[]> = UnionTypeResolve<TAccessorKeys<T, K>>

// // prettier-ignore
// export namespace Accessor {
// function Property(T: TProperties, K: PropertyKey): TSchema {
// return K in T ? T[K as string] : Type.Never()
// }
// function TLiteral(schema: TLiteral, options: IndexerOptions): string[] {
// return [schema.const.toString()]
// function Tuple(T: TSchema[], K: PropertyKey): TSchema {
// return K in T ? T[K as number] : K === 'number' ? Union.Resolve(T) : Type.Never()
// }
// function TNumber(schema: TNumber, options: IndexerOptions): string[] {
// return ['number']
// function Array(T: TSchema, K: PropertyKey): TSchema {
// return K === 'number' ? T : Type.Never()
// }
// function TInteger(schema: TInteger, options: IndexerOptions): string[] {
// return ['number']
// 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)]
// })
// : []
// }
// function Visit(schema: TSchema, options: IndexerOptions): string[] {
// function Key(T: TSchema, K: PropertyKey) {
// return (
// TypeGuard.TTemplateLiteral(schema) ? TTemplateLiteral(schema, options) :
// TypeGuard.TIntersect(schema) ? TIntersect(schema, options) :
// TypeGuard.TUnion(schema) ? TUnion(schema, options) :
// TypeGuard.TObject(schema) ? TObject(schema, options) :
// TypeGuard.TRecord(schema) ? TRecord(schema, options) :
// TypeGuard.TLiteral(schema) ? TLiteral(schema, options) :
// TypeGuard.TInteger(schema) ? TInteger(schema, options) :
// TypeGuard.TNumber(schema) ? TNumber(schema, options) :
// []
// TypeGuard.TIntersect(T) ? Intersect.Resolve(Rest(T.allOf, K)) :
// TypeGuard.TUnion(T) ? Union.Resolve(Rest(T.anyOf, K)) :
// TypeGuard.TTuple(T) ? Tuple(T.items ?? [], K) :
// TypeGuard.TArray(T) ? Array(T.items, K) :
// TypeGuard.TObject(T) ? Property(T.properties, K) :
// Type.Never()
// )
// }
// /** Resolves a regular expression pattern matching all keys in this schema */
// export function Pattern(schema: TSchema): string {
// const keys = Keys(schema, { includePatterns: true })
// const pattern = keys.map((key) => `(${UnwrapPattern(key)})`)
// return `^(${pattern.join('|')})$`
// function Keys(T: TSchema, K: PropertyKey[]): TSchema[] {
// const [L, ...R] = K
// return (K.length > 0)
// ? [Key(T, L), ...Keys(T, R)]
// : []
// }
// /** Resolves an array of keys derived from this schema */
// export function Keys(schema: TSchema, options: IndexerOptions): string[] {
// console.log('Indexer', Visit(schema, options))
// return [...new Set(Visit(schema, options))]
// export function Resolve(schema: TSchema, keys: PropertyKey[]) {
// return Union.Resolve(Keys(schema, keys))
// }
// }

// const T = Type.Object({
// 0: Type.Number(),
// 1: Type.String(),
// 2: Type.Boolean(),
// })
// const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(2)]))
// const T = Type.Composite([
// Type.Object({ x: Type.Optional(Type.Number()) }),
// Type.Object({ x: Type.Optional(Type.Number()) })
// ])

// const A = Accessor.Resolve(T, ['x'])
// const I = Type.Index(T, ['x'])



// console.log(I)
// console.log(A)
101 changes: 101 additions & 0 deletions examples/next/accessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// // --------------------------------------------------------------------------
// // Accessor
// // --------------------------------------------------------------------------

// import { Type, Optional, TObject, TTuple, TIntersect, TUnion, Kind, TSchema, TypeClone, TypeGuard, ValueGuard, TPropertyKey, SchemaOptions, TProperties, TNever, IntersectType, TRecursive, TArray, UnionTypeResolve } from "@sinclair/typebox"

// // ------------------------------------------------------------------
// // TAccessor
// // ------------------------------------------------------------------
// export type TAccessorProperty<T extends TProperties, K extends PropertyKey> = K extends keyof T ? T[K] : TNever
// export type TAccessorTuple<T extends TSchema[], K extends PropertyKey> = K extends keyof T ? T[K] : K extends 'number' ? UnionTypeResolve<T> : TNever
// export type TAccessorArray<T extends TSchema, K extends PropertyKey> = K extends number ? T : TNever
// // prettier-ignore
// export type TAccessorRest<T extends TSchema[], K extends PropertyKey> =
// T extends [infer L extends TSchema, ...infer R extends TSchema[]]
// ? TIndexKey<L, K> extends infer N extends TSchema
// ? N extends TNever
// ? [...TAccessorRest<R, K>]
// : [N, ...TAccessorRest<R, K>]
// : []
// : []
// // prettier-ignore
// export type TIndexKey<T extends TSchema, K extends PropertyKey> =
// T extends TRecursive<infer S> ? TIndexKey<S, K> :
// T extends TIntersect<infer S> ? IntersectType.Resolve<TAccessorRest<S, K>> :
// T extends TUnion<infer S> ? UnionTypeResolve<TAccessorRest<S, K>> :
// T extends TTuple<infer S> ? TAccessorTuple<S, K> :
// T extends TArray<infer S> ? TAccessorArray<S, K> :
// T extends TObject<infer S> ? TAccessorProperty<S, K> :
// TNever
// // prettier-ignore
// export type TAccessorKeys<T extends TSchema, K extends PropertyKey[]> =
// K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
// ? [TIndexKey<T, L>, ...TAccessorKeys<T, R>]
// : []

// export type TAccessor<T extends TSchema, K extends PropertyKey[]> = UnionTypeResolve<TAccessorKeys<T, K>>

// // prettier-ignore
// export namespace Accessor {
// function OptionalUnwrap(schema: TSchema[]): TSchema[] {
// return schema.map((schema) => {
// const { [Optional]: _, ...clone } = TypeClone.Type(schema)
// return clone
// })
// }
// function IsIntersectOptional(schema: TSchema[]): boolean {
// return schema.every((schema) => TypeGuard.TOptional(schema))
// }
// function IsUnionOptional(schema: TSchema[]): boolean {
// return schema.some((schema) => TypeGuard.TOptional(schema))
// }
// function ResolveIntersect(schema: TIntersect): TSchema {
// return IsIntersectOptional(schema.allOf) ? Type.Optional(Type.Intersect(OptionalUnwrap(schema.allOf))) : schema
// }
// function ResolveUnion(schema: TUnion): TSchema {
// return IsUnionOptional(schema.anyOf) ? Type.Optional(Type.Union(OptionalUnwrap(schema.anyOf))) : schema
// }
// function ResolveOptional(schema: TSchema) {
// return (
// schema[Kind] === 'Intersect' ? ResolveIntersect(schema as TIntersect) :
// schema[Kind] === 'Union' ? ResolveUnion(schema as TUnion) :
// schema
// )
// }
// function TIntersect(schema: TIntersect, key: string): TSchema {
// const resolved = schema.allOf.reduce((acc, schema) => {
// const indexed = Visit(schema, key)
// return indexed[Kind] === 'Never' ? acc : [...acc, indexed]
// }, [] as TSchema[])
// return ResolveOptional(Type.Intersect(resolved))
// }
// function TUnion(schema: TUnion, key: string): TSchema {
// const resolved = schema.anyOf.map((schema) => Visit(schema, key))
// return ResolveOptional(Type.Union(resolved))
// }
// function TObject(schema: TObject, key: string): TSchema {
// const property = schema.properties[key]
// return ValueGuard.IsUndefined(property) ? Type.Never() : Type.Union([property])
// }
// function TTuple(schema: TTuple, key: string): TSchema {
// const items = schema.items
// if (ValueGuard.IsUndefined(items)) return Type.Never()
// const element = items[key as any as number] //
// if (ValueGuard.IsUndefined(element)) return Type.Never()
// return element
// }
// function Visit(schema: TSchema, key: string): TSchema {
// return (
// schema[Kind] === 'Intersect' ? TIntersect(schema as TIntersect, key) :
// schema[Kind] === 'Union' ? TUnion(schema as TUnion, key) :
// schema[Kind] === 'Object' ? TObject(schema as TObject, key) :
// schema[Kind] === 'Tuple' ? TTuple(schema as TTuple, key) :
// Type.Never()
// )
// }
// export function Resolve(schema: TSchema, keys: TPropertyKey[], options: SchemaOptions = {}): TSchema {
// const resolved = keys.map((key) => Visit(schema, key.toString()))
// return ResolveOptional(Type.Union(resolved, options))
// }
// }
33 changes: 33 additions & 0 deletions examples/next/distinct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// ------------------------------------------------------------------
// Distinct
// ------------------------------------------------------------------
import { TSchema, TypeExtends, TypeExtendsResult } from "@sinclair/typebox"

// prettier-ignore
export type TDistinctIncludes<T extends TSchema[], C extends TSchema> =
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? C extends L
? true
: TDistinctIncludes<R, C>
: false
// prettier-ignore
export type TDistinct<T extends TSchema[], Acc extends TSchema[] = []> =
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? TDistinctIncludes<Acc, L> extends false
? TDistinct<R, [...Acc, L]>
: TDistinct<R, [...Acc]>
: Acc

export namespace Distinct {
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]): TDistinct<T> {
return schemas.reduce((acc, candidate) => {
return !DistinctIncludes(acc, candidate)
? [...acc, candidate]
: [...acc]
}, [] as TSchema[]) as TDistinct<T>
}
}
Loading

0 comments on commit f8935c4

Please sign in to comment.