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 24, 2023
1 parent 619d9b1 commit 9c885de
Show file tree
Hide file tree
Showing 8 changed files with 387 additions and 89 deletions.
175 changes: 142 additions & 33 deletions examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
AccessResolver,
IndexResolver,
Assert,
UnionToIntersect,
} from '@sinclair/typebox'

// prettier-ignore
Expand All @@ -57,7 +58,7 @@ export namespace KeyResolver {
? [...Resolve<L>, ...Intersect<R>]
: []
)
export function Intersect<const T extends TSchema[]>(T: T): Intersect<T> {
export function Intersect<T extends TSchema[]>(T: [...T]): Intersect<T> {
const [L, ...R] = T
return (
T.length > 0
Expand All @@ -66,32 +67,87 @@ export namespace KeyResolver {
) as Intersect<T>
}
// ----------------------------------------------------------------
// Union
// UnionKeyIn
// ----------------------------------------------------------------
export type Union<T extends TSchema[]> = []
export function Union(T: TSchema[]): string[] {
return globalThis.Object.getOwnPropertyNames(T)
export type UnionKeyIn<T extends PropertyKey[], S extends PropertyKey> = (
T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
? S extends L
? true
: UnionKeyIn<R, S>
: false
)
export function UnionKeyIn<T extends PropertyKey[], S extends PropertyKey>(T: [...T], S: S) {
return (
T.includes(S)
) as UnionKeyIn<T, S>
}
// ----------------------------------------------------------------
// Array
// UnionKeysIn
// ----------------------------------------------------------------
export type Properties<T extends TProperties> = (
UnionToTuple<keyof T>
export type UnionKeysIn<T extends PropertyKey[], S extends PropertyKey[]> = (
T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
? UnionKeyIn<S, L> extends true
? [L, ...UnionKeysIn<R, S>]
: [...UnionKeysIn<R, S>]
: []
)
export function Properties<T extends TProperties>(T: T): Properties<T> {
export function UnionKeysIn<T extends PropertyKey[], S extends PropertyKey[]>(T: [...T], S: [...S]): UnionKeysIn<T, S> {
const [L, ...R] = T
return (
globalThis.Object.getOwnPropertyNames(T)
) as Properties<T>
T.length > 0
? UnionKeyIn(S, L) === true
? [L, ...UnionKeysIn(R, S)]
: [...UnionKeysIn(R, S)]
: []
) as UnionKeysIn<T, S>
}
// ----------------------------------------------------------------
// Array
// UnionKeys
// ----------------------------------------------------------------
export type Array<_ extends TSchema> = (
['number']
export type UnionKeys<T extends PropertyKey[][]> = (
T extends [infer L extends PropertyKey[]]
? L
: T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]]
? UnionKeysIn<L, UnionKeys<R>>
: []
)
export function UnionKeys<T extends PropertyKey[][]>(T: [...T]): UnionKeys<T> {
return (
T.length === 1
? T[0]
: Into(() => {
const [L, ...R] = T
return L.length > 0
? UnionKeysIn(L, UnionKeys(R))
: []
})
) as UnionKeys<T>
}
// ----------------------------------------------------------------
// UnionCollect
// ----------------------------------------------------------------
export type UnionCollect<T extends TSchema[]> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? [Resolve<L>, ...UnionCollect<R>]
: []
)
export function Array<_ extends TSchema>(_: _): Array<_> {
export function UnionCollect<T extends TSchema[]>(T: [...T]): UnionCollect<T> {
const [L, ...R] = T
return (
['number']
T.length > 0
? [Resolve(L), ...UnionCollect(R)]
: []
) as UnionCollect<T>
}
// ----------------------------------------------------------------
// Union
// ----------------------------------------------------------------
export type Union<T extends TSchema[], C extends PropertyKey[][] = UnionCollect<T>> = (
UnionKeys<C>
)
export function Union<T extends TSchema[]>(T: [...T], C = UnionCollect(T)): Union<T> {
return (
UnionKeys(C as PropertyKey[][]) as Union<T>
)
}
// ----------------------------------------------------------------
Expand All @@ -107,49 +163,102 @@ export namespace KeyResolver {
? Tuple<R, [TupleNext<I>, ...I]>
: I
)
export function Tuple<const T extends TSchema[]>(T: T): Tuple<T> {
export function Tuple<T extends TSchema[]>(T: [...T]): Tuple<T> {
return (
T.map((_, index) => index.toString())
) as Tuple<T>
}
// ----------------------------------------------------------------
// Array
// ----------------------------------------------------------------
export type Array<_ extends TSchema> = (
['number']
)
export function Array<_ extends TSchema>(_: _): Array<_> {
return (
['number']
)
}
// ----------------------------------------------------------------
// Properties
// ----------------------------------------------------------------
export type Properties<T extends TProperties> = (
UnionToTuple<keyof T>
)
export function Properties<T extends TProperties>(T: T): Properties<T> {
return (
globalThis.Object.getOwnPropertyNames(T)
) as Properties<T>
}
// ----------------------------------------------------------------
// Pattern
// ----------------------------------------------------------------
function PatternProperties(patternProperties: Record<PropertyKey, TSchema>): string[] {
if(!includePatternProperties) return []
const patternPropertyKeys = globalThis.Object.getOwnPropertyNames(patternProperties)
return patternPropertyKeys.map(key => {
return (key[0] === '^' && key[key.length - 1] === '$')
? key.slice(1, key.length - 1)
: key
})
}
// ----------------------------------------------------------------
// Resolve
// ----------------------------------------------------------------
export type Resolve<T extends TSchema> = (
T extends TRecursive<infer S> ? Resolve<S> :
T extends TIntersect<infer S> ? Intersect<S> :
T extends TUnion<infer S> ? Union<S> :
T extends TObject<infer S> ? Properties<S> :
T extends TArray<infer S> ? Array<S> :
T extends TTuple<infer S> ? Tuple<S> :
T extends TArray<infer S> ? Array<S> :
T extends TObject<infer S> ? Properties<S> :
[]
)
/** Resolves finite keys from this type */
export function Resolve<T extends TSchema>(T: T): Resolve<T> {
return (
TypeGuard.TIntersect(T) ? Intersect(T.allOf) :
TypeGuard.TUnion(T) ? Union(T.anyOf) :
TypeGuard.TObject(T) ? Object(T.properties) :
TypeGuard.TArray(T) ? Array(T.items) :
TypeGuard.TTuple(T) ? Tuple(T.items ?? []) :
TypeGuard.TArray(T) ? Array(T.items) :
TypeGuard.TObject(T) ? Properties(T.properties) :
TypeGuard.TRecord(T) ? PatternProperties(T.patternProperties) :
[]
)
) as Resolve<T>
}
// ----------------------------------------------------------------
// ResolvePattern
// ----------------------------------------------------------------
let includePatternProperties = false
/** Resolves keys as a regular expression, including pattern property keys */
export function ResolvePattern(schema: TSchema): string {
includePatternProperties = true
const keys = Resolve(schema)
includePatternProperties = false
const pattern = keys.map((key) => `(${key})`)
return `^(${pattern.join('|')})$`
}
}

const K = KeyResolver.Resolve(Type.Object({
x: Type.Number()
}))
const A = KeyResolver.ResolvePattern(Type.Intersect([
Type.Record(Type.Number(), Type.Number()),
Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number()
})
]))

const B = KeyResolver.Union([
Type.Object({ x: Type.Number(), y: Type.Number() }),
Type.Object({ x: Type.Number(), y: Type.Number() })
])

console.log(A)

const I = IndexResolver.Resolve(Type.TemplateLiteral('a${1|2|3}'))

const X = AccessResolver.Union([Type.Object({
a1: Type.String()
}), Type.Object({
a2: Type.Number()
})] , 'a1')


type A = { x: number } | { x: number }
type K = keyof A



2 changes: 1 addition & 1 deletion examples/next/indexer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TypeSystem } from '@sinclair/typebox/system'
import { TypeCompiler } from '@sinclair/typebox/compiler'
import { Value, ValuePointer } from '@sinclair/typebox/value'
import { Type, AccessResolver, TemplateLiteralParser, TemplateLiteralFinite, TemplateLiteralResolver, TypeGuard, TIntersect, TUnion, TTemplateLiteral, IsTemplateLiteralFiniteCheck, UnionToTuple, Static, TSchema, TLiteralValue, TLiteral, TNumber, TInteger, TBigInt, TString, KeyResolverOptions, PatternNumber } from '@sinclair/typebox'
import { Type, AccessResolver, TemplateLiteralParser, TemplateLiteralFinite, TemplateLiteralResolver, TypeGuard, TIntersect, TUnion, TTemplateLiteral, IsTemplateLiteralFiniteCheck, UnionToTuple, Static, TSchema, TLiteralValue, TLiteral, TNumber, TInteger, TBigInt, TString, PatternNumber } from '@sinclair/typebox'
import { TObject } from '@sinclair/typebox'

const A = Type.Index(Type.Object({
Expand Down
4 changes: 2 additions & 2 deletions examples/prototypes/evaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
TTuple,
TProperties,
TIntersect,
IntersectTypeResolve,
IntersectType,
TUnion,
TNever
} from '@sinclair/typebox'
Expand Down Expand Up @@ -75,7 +75,7 @@ export type TEvaluateArray<T extends TSchema[]> = T extends [infer L, ...infer
[]
// prettier-ignore
export type TEvaluate<T extends TSchema> =
T extends TIntersect<infer S> ? IntersectTypeResolve<TEvaluateIntersectRest<S>> :
T extends TIntersect<infer S> ? IntersectType.Resolve<TEvaluateIntersectRest<S>> :
T extends TUnion<infer S> ? TUnion<TEvaluateArray<S>> :
T extends TConstructor<infer P, infer R> ? TConstructor<TEvaluateArray<P>, TEvaluate<R>> :
T extends TFunction<infer P, infer R> ? TFunction<TEvaluateArray<P>, TEvaluate<R>> :
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,11 @@ export namespace TypeCompiler {
function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: string): IterableIterator<string> {
const check1 = schema.allOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)).join(' && ')
if (schema.unevaluatedProperties === false) {
const keyCheck = CreateVariable(`${new RegExp(Types.KeyResolver.Pattern(schema))};`)
const keyCheck = CreateVariable(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`)
const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key))`
yield `(${check1} && ${check2})`
} else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) {
const keyCheck = CreateVariable(`${new RegExp(Types.KeyResolver.Pattern(schema))};`)
const keyCheck = CreateVariable(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`)
const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, `${value}[key]`)})`
yield `(${check1} && ${check2})`
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/errors/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,15 +262,15 @@ function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], path
}
}
if (schema.unevaluatedProperties === false) {
const keyCheck = new RegExp(Types.KeyResolver.Pattern(schema))
const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema))
for (const valueKey of Object.getOwnPropertyNames(value)) {
if (!keyCheck.test(valueKey)) {
yield Create(ValueErrorType.IntersectUnevaluatedProperties, schema, `${path}/${valueKey}`, value)
}
}
}
if (typeof schema.unevaluatedProperties === 'object') {
const keyCheck = new RegExp(Types.KeyResolver.Pattern(schema))
const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema))
for (const valueKey of Object.getOwnPropertyNames(value)) {
if (!keyCheck.test(valueKey)) {
const next = Visit(schema.unevaluatedProperties, references, `${path}/${valueKey}`, value[valueKey]).next()
Expand Down
Loading

0 comments on commit 9c885de

Please sign in to comment.