Skip to content

Commit

Permalink
Implement Computed Index
Browse files Browse the repository at this point in the history
  • Loading branch information
sinclairzx81 committed Nov 16, 2024
1 parent 0e10bb2 commit 37be0c1
Show file tree
Hide file tree
Showing 17 changed files with 437 additions and 358 deletions.
47 changes: 30 additions & 17 deletions example/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,47 @@ import { TypeSystem } from '@sinclair/typebox/system'
import { TypeCompiler } from '@sinclair/typebox/compiler'
import { Value, ValuePointer } from '@sinclair/typebox/value'
import { Parse, StaticParseAsType } from '@sinclair/typebox/syntax'
import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox'
import { Type, Index, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox'

// KindGuard.IsLiteralValue

// New Types:
// Computed Types:
// - Awaited (Computed)
// - Partial (Computed)
// - Pick (Computed)
// - Omit (Computed)
// - Required (Computed)
// - KeyOf
// - KeyOf (Computed)
// - Index (Computed ... nearly)
// - Record (...)

const T = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
})



// const Module = Parse(`module {

// type A = {
// x: number
// y: number
// z: number
// }

// type K = A[keyof A]

// }`)

const Module = Type.Module({
A: Type.Object({
x: Type.Number(),
y: Type.Number(),
y: Type.String(),
z: Type.Boolean(),
}),
T: Type.KeyOf(Type.Ref('A')),
K: Type.Index(Type.Ref('A'), Type.KeyOf(Type.Ref('A')))
})

const A = Module.Import('A')
const T = Module.Import('T')

function test(value: Static<typeof T>) {}

console.dir(T, { depth: 100 })

type A = Static<typeof A>
type T = Static<typeof T>
const K = Module.Import('K')

// Ok(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: [] }] }] })
type K = Static<typeof K>
5 changes: 3 additions & 2 deletions src/syntax/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,11 +324,12 @@ const FactorIndexArray = (Type: Types.TSchema, IndexArray: unknown[]): Types.TSc
const [Left, Right] = DestructureRight(IndexArray) as [unknown[], Types.TSchema[]]
return (
!Types.ValueGuard.IsUndefined(Right) ? (
Right.length === 1 ? Types.Index(FactorIndexArray(Type, Left), Right[0]) :
// note: Indexed types require reimplementation to replace `[number]` indexers
Right.length === 1 ? Types.Index(FactorIndexArray(Type, Left), Right[0]) as never :
Right.length === 0 ? Types.Array(FactorIndexArray(Type, Left)) :
Types.Never()
) : Type
)
)
}
// prettier-ignore
const FactorMapping = (KeyOf: boolean, Type: Types.TSchema, IndexArray: unknown[], Extends: Types.TSchema[]) => {
Expand Down
2 changes: 1 addition & 1 deletion src/type/array/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ export interface TArray<T extends TSchema = TSchema> extends TSchema, ArrayOptio
items: T
}
/** `[Json]` Creates an Array type */
export function Array<T extends TSchema>(items: T, options?: ArrayOptions): TArray<T> {
export function Array<Type extends TSchema>(items: Type, options?: ArrayOptions): TArray<Type> {
return CreateType({ [Kind]: 'Array', type: 'array', items }, options) as never
}
63 changes: 24 additions & 39 deletions src/type/indexed/indexed-from-mapped-key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,68 +37,53 @@ import { Clone } from '../clone/value'
// MappedIndexPropertyKey
// ------------------------------------------------------------------
// prettier-ignore
type TMappedIndexPropertyKey<
T extends TSchema,
K extends PropertyKey
> = {
[_ in K]: TIndex<T, [K]>
type TMappedIndexPropertyKey<Type extends TSchema, Key extends PropertyKey> = {
[_ in Key]: TIndex<Type, [Key]>
}
// prettier-ignore
function MappedIndexPropertyKey<
T extends TSchema,
K extends PropertyKey
>(T: T, K: K, options?: SchemaOptions): TMappedIndexPropertyKey<T, K> {
return { [K]: Index(T, [K], Clone(options)) } as never
function MappedIndexPropertyKey<Type extends TSchema, Key extends PropertyKey
>(type: Type, key: Key, options?: SchemaOptions): TMappedIndexPropertyKey<Type, Key> {
return { [key]: Index(type, [key], Clone(options)) } as never
}
// ------------------------------------------------------------------
// MappedIndexPropertyKeys
// ------------------------------------------------------------------
// prettier-ignore
type TMappedIndexPropertyKeys<T extends TSchema, K extends PropertyKey[], Acc extends TProperties = {}> = (
K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
? TMappedIndexPropertyKeys<T, R, Acc & TMappedIndexPropertyKey<T, L>>
: Acc
type TMappedIndexPropertyKeys<Type extends TSchema, PropertyKeys extends PropertyKey[], Result extends TProperties = {}> = (
PropertyKeys extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]]
? TMappedIndexPropertyKeys<Type, Right, Result & TMappedIndexPropertyKey<Type, Left>>
: Result
)
// prettier-ignore
function MappedIndexPropertyKeys<
T extends TSchema,
K extends PropertyKey[]
>(T: T, K: [...K], options?: SchemaOptions): TMappedIndexPropertyKeys<T, K> {
return K.reduce((Acc, L) => {
return { ...Acc, ...MappedIndexPropertyKey(T, L, options) }
function MappedIndexPropertyKeys<Type extends TSchema, PropertyKeys extends PropertyKey[]>(type: Type, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TMappedIndexPropertyKeys<Type, PropertyKeys> {
return propertyKeys.reduce((result, left) => {
return { ...result, ...MappedIndexPropertyKey(type, left, options) }
}, {} as TProperties) as never
}
// ------------------------------------------------------------------
// MappedIndexProperties
// ------------------------------------------------------------------
// prettier-ignore
type TMappedIndexProperties<T extends TSchema, K extends TMappedKey> = Evaluate<
TMappedIndexPropertyKeys<T, K['keys']>
type TMappedIndexProperties<Type extends TSchema, MappedKey extends TMappedKey> = Evaluate<
TMappedIndexPropertyKeys<Type, MappedKey['keys']>
>
// prettier-ignore
function MappedIndexProperties<
T extends TSchema,
K extends TMappedKey
>(T: T, K: K, options?: SchemaOptions): TMappedIndexProperties<T, K> {
return MappedIndexPropertyKeys(T, K.keys, options) as never
function MappedIndexProperties<Type extends TSchema, MappedKey extends TMappedKey>(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TMappedIndexProperties<Type, MappedKey> {
return MappedIndexPropertyKeys(type, mappedKey.keys, options) as never
}
// ------------------------------------------------------------------
// TIndexFromMappedKey
// ------------------------------------------------------------------
// prettier-ignore
export type TIndexFromMappedKey<
T extends TSchema,
K extends TMappedKey,
P extends TProperties = TMappedIndexProperties<T, K>
export type TIndexFromMappedKey<Type extends TSchema, MappedKey extends TMappedKey,
Properties extends TProperties = TMappedIndexProperties<Type, MappedKey>
> = (
Ensure<TMappedResult<P>>
Ensure<TMappedResult<Properties>>
)
// prettier-ignore
export function IndexFromMappedKey<
T extends TSchema,
K extends TMappedKey,
P extends TProperties = TMappedIndexProperties<T, K>
>(T: T, K: K, options?: SchemaOptions): TMappedResult<P> {
const P = MappedIndexProperties(T, K, options)
return MappedResult(P) as never
export function IndexFromMappedKey<Type extends TSchema, MappedKey extends TMappedKey,
Properties extends TProperties = TMappedIndexProperties<Type, MappedKey>
>(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TMappedResult<Properties> {
const properties = MappedIndexProperties(type, mappedKey, options)
return MappedResult(properties) as never
}
21 changes: 11 additions & 10 deletions src/type/indexed/indexed-from-mapped-result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,22 @@ import { Index, type TIndex } from './index'
// ------------------------------------------------------------------
// prettier-ignore
type TFromProperties<
T extends TSchema,
P extends TProperties
Type extends TSchema,
Properties extends TProperties
> = (
{ [K2 in keyof P]: TIndex<T, TIndexPropertyKeys<P[K2]>> }
{ [K2 in keyof Properties]: TIndex<Type, TIndexPropertyKeys<Properties[K2]>> }
)
// prettier-ignore
function FromProperties<
T extends TSchema,
P extends TProperties
>(T: T, P: P, options?: SchemaOptions): TFromProperties<T, P> {
const Acc = {} as Record<PropertyKey, TSchema>
for(const K2 of Object.getOwnPropertyNames(P)) {
Acc[K2] = Index(T, IndexPropertyKeys(P[K2]), options)
Type extends TSchema,
Properties extends TProperties
>(type: Type, properties: Properties, options?: SchemaOptions): TFromProperties<Type, Properties> {
const result = {} as Record<PropertyKey, TSchema>
for(const K2 of Object.getOwnPropertyNames(properties)) {
const keys = IndexPropertyKeys(properties[K2])
result[K2] = Index(type, keys, options) as never
}
return Acc as never
return result as never
}
// ------------------------------------------------------------------
// FromMappedResult
Expand Down
60 changes: 31 additions & 29 deletions src/type/indexed/indexed-property-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,38 +41,40 @@ import { IsTemplateLiteral, IsUnion, IsLiteral, IsNumber, IsInteger } from '../g
// FromTemplateLiteral
// ------------------------------------------------------------------
// prettier-ignore
type TFromTemplateLiteral<T extends TTemplateLiteral, R extends string[] = TTemplateLiteralGenerate<T>> = (R)
type TFromTemplateLiteral<TemplateLiteral extends TTemplateLiteral,
Result extends string[] = TTemplateLiteralGenerate<TemplateLiteral>
> = Result
// prettier-ignore
function FromTemplateLiteral<T extends TTemplateLiteral>(T: T): TFromTemplateLiteral<T> {
const R = TemplateLiteralGenerate(T) as string[]
return R.map(S => S.toString()) as never
function FromTemplateLiteral<TemplateLiteral extends TTemplateLiteral>(templateLiteral: TemplateLiteral): TFromTemplateLiteral<TemplateLiteral> {
const result = TemplateLiteralGenerate(templateLiteral) as string[]
return result.map(S => S.toString()) as never
}
// ------------------------------------------------------------------
// FromUnion
// ------------------------------------------------------------------
// prettier-ignore
type TFromUnion<T extends TSchema[], Acc extends string[] = []> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? TFromUnion<R, [...Acc, ...TIndexPropertyKeys<L>]>
: Acc
type TFromUnion<Types extends TSchema[], Result extends string[] = []> = (
Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]]
? TFromUnion<Right, [...Result, ...TIndexPropertyKeys<Left>]>
: Result
)
// prettier-ignore
function FromUnion<T extends TSchema[]>(T: T): TFromUnion<T> {
const Acc = [] as string[]
for(const L of T) Acc.push(...IndexPropertyKeys(L))
return Acc as never
function FromUnion<Type extends TSchema[]>(type: Type): TFromUnion<Type> {
const result = [] as string[]
for(const left of type) result.push(...IndexPropertyKeys(left))
return result as never
}
// ------------------------------------------------------------------
// FromLiteral
// ------------------------------------------------------------------
// prettier-ignore
type TFromLiteral<T extends TLiteralValue> = (
T extends PropertyKey
? [`${T}`]
type TFromLiteral<LiteralValue extends TLiteralValue> = (
LiteralValue extends PropertyKey
? [`${LiteralValue}`]
: []
)
// prettier-ignore
function FromLiteral<T extends TLiteralValue>(T: T): TFromLiteral<T> {
function FromLiteral<LiteralValue extends TLiteralValue>(T: LiteralValue): TFromLiteral<LiteralValue> {
return (
[(T as string).toString()] // TS 5.4 observes TLiteralValue as not having a toString()
) as never
Expand All @@ -81,23 +83,23 @@ function FromLiteral<T extends TLiteralValue>(T: T): TFromLiteral<T> {
// IndexedKeyResolve
// ------------------------------------------------------------------
// prettier-ignore
export type TIndexPropertyKeys<T extends TSchema> = (
T extends TTemplateLiteral ? TFromTemplateLiteral<T> :
T extends TUnion<infer S> ? TFromUnion<S> :
T extends TLiteral<infer S> ? TFromLiteral<S> :
T extends TNumber ? ['[number]'] :
T extends TInteger ? ['[number]'] :
export type TIndexPropertyKeys<Type extends TSchema, Result extends PropertyKey[] = (
Type extends TTemplateLiteral ? TFromTemplateLiteral<Type> :
Type extends TUnion<infer Types extends TSchema[]> ? TFromUnion<Types> :
Type extends TLiteral<infer LiteralValue extends TLiteralValue> ? TFromLiteral<LiteralValue> :
Type extends TNumber ? ['[number]'] :
Type extends TInteger ? ['[number]'] :
[]
)
)> = Result
/** Returns a tuple of PropertyKeys derived from the given TSchema */
// prettier-ignore
export function IndexPropertyKeys<T extends TSchema>(T: T): TIndexPropertyKeys<T> {
export function IndexPropertyKeys<Type extends TSchema>(type: Type): TIndexPropertyKeys<Type> {
return [...new Set<string>((
IsTemplateLiteral(T) ? FromTemplateLiteral(T) :
IsUnion(T) ? FromUnion(T.anyOf) :
IsLiteral(T) ? FromLiteral(T.const) :
IsNumber(T) ? ['[number]'] :
IsInteger(T) ? ['[number]'] :
IsTemplateLiteral(type) ? FromTemplateLiteral(type) :
IsUnion(type) ? FromUnion(type.anyOf) :
IsLiteral(type) ? FromLiteral(type.const) :
IsNumber(type) ? ['[number]'] :
IsInteger(type) ? ['[number]'] :
[]
))] as never
}
Loading

0 comments on commit 37be0c1

Please sign in to comment.