Skip to content

Commit

Permalink
Reimplement Indexed Accessors
Browse files Browse the repository at this point in the history
  • Loading branch information
sinclairzx81 committed Nov 26, 2023
1 parent 2a62259 commit 7df9219
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 106 deletions.
17 changes: 6 additions & 11 deletions examples/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { Value } from '@sinclair/typebox/value'
import { TSchema, Type, TypeGuard, Discard, Evaluate, Ensure, TObject, Kind } from '@sinclair/typebox'
import { PickResolver, IndexedKeyResolver } from '@sinclair/typebox/type'

const L = Type.Union([Type.Literal('x'), Type.Literal('y'), Type.Literal('z')])
const K = Type.TemplateLiteral('${0|1}${0|1}${0|1}${0|1}${0|1}')

const T = Type.Pick(
Type.Record(K, Type.Number()),
K,
)
console.log(T)
import { TSchema, Type, Static, TypeGuard, Discard, Evaluate, Ensure, TObject, Kind } from '@sinclair/typebox'
import { PartialResolver, IndexedKeyResolver } from '@sinclair/typebox/type'

const A = Type.Object({ x: Type.Number() })
const B = Type.Object({ y: Type.Number() })
const I = Type.Intersect([A, B])
const T = Type.Partial(I)

console.log(T)
2 changes: 1 addition & 1 deletion src/type/guard/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ export namespace TypeGuard {
'Boolean',
'Constructor',
'Date',
'Enum',
'Function',
'Integer',
'Intersect',
'Iterator',
'Literal',
'Never',
'Not',
'Null',
'Number',
Expand Down
1 change: 1 addition & 0 deletions src/type/resolve/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export * from './keyof/index'
export * from './logic/index'
export * from './modifiers/index'
export * from './omit/index'
export * from './partial/index'
export * from './pick/index'
export * from './record/index'
export * from './template-literal/index'
Expand Down
50 changes: 17 additions & 33 deletions src/type/resolve/omit/omit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,20 @@ export namespace OmitResolver {
// ----------------------------------------------------------------
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
export type TupleToUnion<T extends PropertyKey[]> =
T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
? L | TupleToUnion<R>
: never
export type TupleToUnion<T extends PropertyKey[]> = T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] ? L | TupleToUnion<R> : never
// ----------------------------------------------------------------
// Intersect
// ----------------------------------------------------------------
export type Intersect<T extends TSchema[], K extends PropertyKey[]> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? [Resolve<L, K>, ...Intersect<R, K>]
: []
)
export type Intersect<T extends TSchema[], K extends PropertyKey[]> = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Resolve<L, K>, ...Intersect<R, K>] : []
export function Intersect<T extends TSchema[], K extends PropertyKey[]>(T: T, K: K) {
return T.map(T => Resolve(T, K)) as Intersect<T, K>
return T.map((T) => Resolve(T, K)) as Intersect<T, K>
}
// ----------------------------------------------------------------
// Union
// ----------------------------------------------------------------
export type Union<T extends TSchema[], K extends PropertyKey[]> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? [Resolve<L, K>, ...Union<R, K>]
: []
)
export type Union<T extends TSchema[], K extends PropertyKey[]> = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Resolve<L, K>, ...Union<R, K>] : []
export function Union<T extends TSchema[], K extends PropertyKey[]>(T: T, K: K) {
return T.map(T => Resolve(T, K)) as Union<T, K>
return T.map((T) => Resolve(T, K)) as Union<T, K>
}
// ----------------------------------------------------------------
// Properties
Expand All @@ -67,9 +56,7 @@ export namespace OmitResolver {
const { [K]: _, ...R } = T
return R as TProperties
}
export type Properties<T extends TProperties, K extends PropertyKey[], I extends PropertyKey = TupleToUnion<K>> = (
Evaluate<Omit<T, I>>
)
export type Properties<T extends TProperties, K extends PropertyKey[], I extends PropertyKey = TupleToUnion<K>> = Evaluate<Omit<T, I>>
export function Properties<T extends TProperties, K extends PropertyKey[]>(T: T, K: K) {
return K.reduce((T, K2) => {
return Property(T, K2)
Expand All @@ -78,19 +65,16 @@ export namespace OmitResolver {
// ----------------------------------------------------------------
// Resolve
// ----------------------------------------------------------------
export type Resolve<T extends TProperties, K extends PropertyKey[]> = (
T extends TRecursive<infer S> ? TRecursive<Resolve<S, K>> :
T extends TIntersect<infer S> ? TIntersect<Intersect<S, K>> :
T extends TUnion<infer S> ? TUnion<Union<S, K>> :
T extends TObject<infer S> ? TObject<Properties<S, K>> :
TObject<{}>
)
export type Resolve<T extends TProperties, K extends PropertyKey[]> = T extends TRecursive<infer S>
? TRecursive<Resolve<S, K>>
: T extends TIntersect<infer S>
? TIntersect<Intersect<S, K>>
: T extends TUnion<infer S>
? TUnion<Union<S, K>>
: T extends TObject<infer S>
? TObject<Properties<S, K>>
: TObject<{}>
export function Resolve<T extends TSchema, K extends PropertyKey[]>(T: T, K: [...K]): Resolve<T, K> {
return (
TypeGuard.TIntersect(T) ? Type.Intersect(Intersect(T.allOf, K)) :
TypeGuard.TUnion(T) ? Type.Union(Union(T.anyOf, K)) :
TypeGuard.TObject(T) ? Type.Object(Properties(T.properties, K)) :
Type.Object({})
) as Resolve<T, K>
return (TypeGuard.TIntersect(T) ? Type.Intersect(Intersect(T.allOf, K)) : TypeGuard.TUnion(T) ? Type.Union(Union(T.anyOf, K)) : TypeGuard.TObject(T) ? Type.Object(Properties(T.properties, K)) : Type.Object({})) as Resolve<T, K>
}
}
}
29 changes: 29 additions & 0 deletions src/type/resolve/partial/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
@sinclair/typebox
The MIT License (MIT)
Copyright (c) 2017-2023 Haydn Paterson (sinclair) <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---------------------------------------------------------------------------*/

export * from './partial'
95 changes: 95 additions & 0 deletions src/type/resolve/partial/partial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*--------------------------------------------------------------------------
@sinclair/typebox
The MIT License (MIT)
Copyright (c) 2017-2023 Haydn Paterson (sinclair) <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---------------------------------------------------------------------------*/

import { Type, TSchema, TypeGuard, TObject, TProperties, TIntersect, TUnion, TOptional, TRecursive } from '../../../typebox'

// prettier-ignore
export namespace PartialResolver {
export type Evaluate<T> = T extends infer O ? { [K in keyof O]: O[K] } : never
// ----------------------------------------------------------------
// Intersect
// ----------------------------------------------------------------
export type Intersect<T extends TSchema[]> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? [Resolve<L>, ...Intersect<R>]
: []
)
export function Intersect<T extends TSchema[]>(T: [...T]) : Intersect<T> {
const [L, ...R] = T
return (
T.length > 0
? [Resolve(L), ...Intersect(R)]
: []
) as Intersect<T>
}
// ----------------------------------------------------------------
// Union
// ----------------------------------------------------------------
export type Union<T extends TSchema[]> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? [Resolve<L>, ...Union<R>]
: []
)
export function Union<T extends TSchema[]>(T: [...T]): Union<T> {
const [L, ...R] = T
return (
T.length > 0
? [Resolve(L), ...Union(R)]
: []
) as Union<T>
}
// ----------------------------------------------------------------
// Properties
// ----------------------------------------------------------------
export type Properties<T extends TProperties> = Evaluate<{
[K in keyof T]: TOptional<T[K]>
}>
export function Properties<T extends TProperties>(T: T) {
return globalThis.Object.getOwnPropertyNames(T).reduce((Acc, K) => {
return { ...Acc, [K]: Type.Optional(T[K]) }
}, {} as TProperties)
}
// ----------------------------------------------------------------
// Resolve
// ----------------------------------------------------------------
export type Resolve<T extends TSchema> = (
T extends TRecursive<infer S> ? TRecursive<Resolve<S>> :
T extends TIntersect<infer S> ? TIntersect<Intersect<S>> :
T extends TUnion<infer S> ? TUnion<Union<S>> :
T extends TObject<infer S> ? TObject<Properties<S>> :
TObject<{}>
)
export function Resolve<T extends TSchema>(T: T): Resolve<T> {
return (
TypeGuard.TIntersect(T) ? Type.Intersect(Intersect(T.allOf)) :
TypeGuard.TUnion(T) ? Type.Union(Union(T.anyOf)) :
TypeGuard.TObject(T) ? Type.Object(Properties(T.properties)) :
Type.Object({})
) as Resolve<T>
}
}
54 changes: 18 additions & 36 deletions src/type/resolve/pick/pick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,61 +34,43 @@ export namespace PickResolver {
// ----------------------------------------------------------------
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
export type TupleToUnion<T extends PropertyKey[]> =
T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
? L | TupleToUnion<R>
: never
export type TupleToUnion<T extends PropertyKey[]> = T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] ? L | TupleToUnion<R> : never
// ----------------------------------------------------------------
// Intersect
// ----------------------------------------------------------------
export type Intersect<T extends TSchema[], K extends PropertyKey[]> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? [Resolve<L, K>, ...Intersect<R, K>]
: []
)
export type Intersect<T extends TSchema[], K extends PropertyKey[]> = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Resolve<L, K>, ...Intersect<R, K>] : []
export function Intersect<T extends TSchema[], K extends PropertyKey[]>(T: T, K: K) {
return T.map(T => Resolve(T, K)) as Intersect<T, K>
return T.map((T) => Resolve(T, K)) as Intersect<T, K>
}
// ----------------------------------------------------------------
// Union
// ----------------------------------------------------------------
export type Union<T extends TSchema[], K extends PropertyKey[]> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? [Resolve<L, K>, ...Union<R, K>]
: []
)
export type Union<T extends TSchema[], K extends PropertyKey[]> = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Resolve<L, K>, ...Union<R, K>] : []
export function Union<T extends TSchema[], K extends PropertyKey[]>(T: T, K: K) {
return T.map(T => Resolve(T, K)) as Union<T, K>
return T.map((T) => Resolve(T, K)) as Union<T, K>
}
// ----------------------------------------------------------------
// Properties
// ----------------------------------------------------------------
export type Properties<T extends TProperties, K extends PropertyKey[], I extends PropertyKey = TupleToUnion<K>> = (
Evaluate<Pick<T, I & keyof T>>
)
export type Properties<T extends TProperties, K extends PropertyKey[], I extends PropertyKey = TupleToUnion<K>> = Evaluate<Pick<T, I & keyof T>>
export function Properties<T extends TProperties, K extends PropertyKey[]>(T: T, K: K) {
return K.reduce((Acc, K) => {
return K in T
? { ...Acc, [K]: T[K as keyof T] }
: Acc
return K in T ? { ...Acc, [K]: T[K as keyof T] } : Acc
}, {})
}
// ----------------------------------------------------------------
// Resolve
// ----------------------------------------------------------------
export type Resolve<T extends TProperties, K extends PropertyKey[]> = (
T extends TRecursive<infer S> ? TRecursive<Resolve<S, K>> :
T extends TIntersect<infer S> ? TIntersect<Intersect<S, K>> :
T extends TUnion<infer S> ? TUnion<Union<S, K>> :
T extends TObject<infer S> ? TObject<Properties<S, K>> :
TObject<{}>
)
export type Resolve<T extends TProperties, K extends PropertyKey[]> = T extends TRecursive<infer S>
? TRecursive<Resolve<S, K>>
: T extends TIntersect<infer S>
? TIntersect<Intersect<S, K>>
: T extends TUnion<infer S>
? TUnion<Union<S, K>>
: T extends TObject<infer S>
? TObject<Properties<S, K>>
: TObject<{}>
export function Resolve<T extends TSchema, K extends PropertyKey[]>(T: T, K: [...K]): Resolve<T, K> {
return (
TypeGuard.TIntersect(T) ? Type.Intersect(Intersect(T.allOf, K)) :
TypeGuard.TUnion(T) ? Type.Union(Union(T.anyOf, K)) :
TypeGuard.TObject(T) ? Type.Object(Properties(T.properties, K)) :
Type.Object({})
) as Resolve<T, K>
return (TypeGuard.TIntersect(T) ? Type.Intersect(Intersect(T.allOf, K)) : TypeGuard.TUnion(T) ? Type.Union(Union(T.anyOf, K)) : TypeGuard.TObject(T) ? Type.Object(Properties(T.properties, K)) : Type.Object({})) as Resolve<T, K>
}
}
}
32 changes: 7 additions & 25 deletions src/typebox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ import {
IntersectResolver,
CompositeResolver,
OmitResolver,
PickResolver
PartialResolver,
PickResolver,
} from './type/resolve/index'
// ------------------------------------------------------------------
// Symbols
Expand Down Expand Up @@ -463,23 +464,7 @@ export type TParameters<T extends TFunction> = Ensure<TTuple<T['parameters']>>
// ------------------------------------------------------------------
// TPartial
// ------------------------------------------------------------------
export type TPartialObjectArray<T extends TObject[]> = AssertRest<{ [K in keyof T]: TPartial<AssertType<T[K], TObject>> }, TObject[]>
export type TPartialRest<T extends TSchema[]> = AssertRest<{ [K in keyof T]: TPartial<AssertType<T[K]>> }>
// prettier-ignore
export type TPartialProperties<T extends TProperties> = Evaluate<AssertProperties<{
[K in keyof T]:
T[K] extends (TReadonlyOptional<infer S>) ? TReadonlyOptional<S> :
T[K] extends (TReadonly<infer S>) ? TReadonlyOptional<S> :
T[K] extends (TOptional<infer S>) ? TOptional<S> :
TOptional<T[K]>
}>>
// prettier-ignore
export type TPartial<T extends TSchema> =
T extends TRecursive<infer S> ? TRecursive<TPartial<S>> :
T extends TIntersect<infer S> ? TIntersect<TPartialRest<S>> :
T extends TUnion<infer S> ? TUnion<TPartialRest<S>> :
T extends TObject<infer S> ? TObject<TPartialProperties<S>> :
T
export type TPartial<T extends TSchema> = PartialResolver.Resolve<T>
// ------------------------------------------------------------------
// TPick
// ------------------------------------------------------------------
Expand Down Expand Up @@ -1038,13 +1023,10 @@ export class JsonTypeBuilder extends TypeBuilder {
return { ...D, ...R }
}
/** `[Json]` Constructs a type where all properties are optional */
public Partial<T extends TSchema>(schema: T, options: ObjectOptions = {}): TPartial<T> {
return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => {
const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => {
return { ...acc, [key]: this.Optional(object.properties[key]) }
}, {} as TProperties)
return this.Object(properties, this.Discard(object, ['required']) /* object used as options to retain other constraints */)
}, options)
public Partial<T extends TSchema>(T: T, options: ObjectOptions = {}): TPartial<T> {
const D = Discard.Keys(T, [ Transform, '$id', 'required' ]) as TSchema
const R = TypeClone.Type(PartialResolver.Resolve(T), options)
return { ...D, ...R }
}
/** `[Json]` Constructs a type whose keys are picked from the given type */
public Pick<T extends TSchema, K extends TSchema, I extends PropertyKey[] = IndexedKeyResolver.Resolve<K>>(T: T, K: K, options?: SchemaOptions): TPick<T, I>
Expand Down

0 comments on commit 7df9219

Please sign in to comment.