-
Notifications
You must be signed in to change notification settings - Fork 170
/
Copy pathmapped.ts
271 lines (261 loc) · 13.1 KB
/
mapped.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/*--------------------------------------------------------------------------
@sinclair/typebox/type
The MIT License (MIT)
Copyright (c) 2017-2024 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 } from '../schema/index'
import type { Ensure, Evaluate, Assert } from '../helpers/index'
import { Kind, OptionalKind, ReadonlyKind } from '../symbols/index'
import { CreateType } from '../create/type'
import { Discard } from '../discard/index'
// evaluation types
import { Array, type TArray } from '../array/index'
import { AsyncIterator, type TAsyncIterator } from '../async-iterator/index'
import { Constructor, type TConstructor } from '../constructor/index'
import { type TEnum, type TEnumRecord } from '../enum/index'
import { Function as FunctionType, type TFunction } from '../function/index'
import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index'
import { Intersect, type TIntersect } from '../intersect/index'
import { Iterator, type TIterator } from '../iterator/index'
import { Literal, type TLiteral, type TLiteralValue } from '../literal/index'
import { Object, type TObject, type TProperties, type ObjectOptions } from '../object/index'
import { Optional, type TOptional } from '../optional/index'
import { Promise, type TPromise } from '../promise/index'
import { Readonly, type TReadonly } from '../readonly/index'
import { Tuple, type TTuple } from '../tuple/index'
import { Union, type TUnion } from '../union/index'
// operator
import { SetIncludes, type TSetIncludes } from '../sets/index'
// mapping types
import { MappedResult, type TMappedResult } from './mapped-result'
import type { TMappedKey } from './mapped-key'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsArray, IsAsyncIterator, IsConstructor, IsFunction, IsIntersect, IsIterator, IsReadonly, IsMappedResult, IsMappedKey, IsObject, IsOptional, IsPromise, IsSchema, IsTuple, IsUnion } from '../guard/kind'
// ------------------------------------------------------------------
// FromMappedResult
//
// We only evaluate the context (K) if it is a keyof P. Otherwise we
// remap the back to a MappedResult with the expectation it will be
// evaluated by an outer context. Note that overlapping keys in the
// outer context may result in incorrect evaluation of the outer
// context. Reproduction code below.
//
// type S = {
// [L in 'A' | 'B']: {
// [R in 'B' | 'C']: [L, R] // issue: overlapping 'B'
// }
// }
//
// ------------------------------------------------------------------
// prettier-ignore
type TFromMappedResult<K extends PropertyKey, P extends TProperties> = (
K extends keyof P
? FromSchemaType<K, P[K]>
: TMappedResult<P>
)
// prettier-ignore
function FromMappedResult<K extends PropertyKey, P extends TProperties>(K: K, P: P): TFromMappedResult<K, P> {
return (
K in P
? FromSchemaType(K, P[K as string])
: MappedResult(P)
) as never
}
// ------------------------------------------------------------------
// MappedKeyToKnownMappedResultProperties
//
// This path is used when K is in the PropertyKey set of P. This is
// the standard (fast) path when not nesting mapped types.
// ------------------------------------------------------------------
// prettier-ignore
type TMappedKeyToKnownMappedResultProperties<K extends PropertyKey> = {
[_ in K]: TLiteral<Assert<K, TLiteralValue>>
}
// prettier-ignore
function MappedKeyToKnownMappedResultProperties<K extends PropertyKey>(K: K): TMappedKeyToKnownMappedResultProperties<K> {
return { [K]: Literal(K as TLiteralValue) } as never
}
// ------------------------------------------------------------------
// MappedKeyToUnknownMappedResultProperties
//
// This path is used when K is outside the set of P. This indicates
// that the K originates from outside the current mappped type. This
// path is very slow as we need to construct a full MappedResult
// to be evaluated by the exterior mapped type.
// ------------------------------------------------------------------
// prettier-ignore
type TMappedKeyToUnknownMappedResultProperties<P extends PropertyKey[], Acc extends TProperties = {}> = (
P extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
? TMappedKeyToUnknownMappedResultProperties<R, Acc & { [_ in L]: TLiteral<Assert<L, TLiteralValue>> }>
: Acc
)
// prettier-ignore
function MappedKeyToUnknownMappedResultProperties<P extends PropertyKey[]>(P: [...P]): TMappedKeyToUnknownMappedResultProperties<P> {
const Acc = {} as Record<PropertyKey, TSchema>
for(const L of P) Acc[L] = Literal(L as TLiteralValue)
return Acc as never
}
// ------------------------------------------------------------------
// MappedKeyToMappedResultProperties
// ------------------------------------------------------------------
// prettier-ignore
type TMappedKeyToMappedResultProperties<K extends PropertyKey, P extends PropertyKey[]> = (
TSetIncludes<P, K> extends true
? TMappedKeyToKnownMappedResultProperties<K>
: TMappedKeyToUnknownMappedResultProperties<P>
)
// prettier-ignore
function MappedKeyToMappedResultProperties<K extends PropertyKey, P extends PropertyKey[]>(K: K, P: [...P]): TMappedKeyToMappedResultProperties<K, P> {
return (
SetIncludes(P, K)
? MappedKeyToKnownMappedResultProperties(K)
: MappedKeyToUnknownMappedResultProperties(P)
) as never
}
// prettier-ignore
type TFromMappedKey<
K extends PropertyKey,
P extends PropertyKey[],
R extends TProperties = TMappedKeyToMappedResultProperties<K, P>
> = (
TFromMappedResult<K, R>
)
// prettier-ignore
function FromMappedKey<K extends PropertyKey, P extends PropertyKey[]>(K: K, P: [...P]): TFromMappedKey<K, P> {
const R = MappedKeyToMappedResultProperties(K, P)
return FromMappedResult(K, R) as never
}
// ------------------------------------------------------------------
// FromRest
// ------------------------------------------------------------------
// prettier-ignore
type TFromRest<K extends PropertyKey, T extends TSchema[], Acc extends TSchema[] = []> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? TFromRest<K, R, [...Acc, FromSchemaType<K, L>]>
: Acc
)
// prettier-ignore
function FromRest<K extends PropertyKey, T extends TSchema[]>(K: K, T: [...T]): TFromRest<K, T> {
return T.map(L => FromSchemaType(K, L)) as never
}
// ------------------------------------------------------------------
// FromProperties
// ------------------------------------------------------------------
// prettier-ignore
type FromProperties<K extends PropertyKey, T extends TProperties, R extends TProperties = Evaluate<{
[K2 in keyof T]: FromSchemaType<K, T[K2]>
}>> = R
// prettier-ignore
function FromProperties<K extends PropertyKey, T extends TProperties>(K: K, T: T): FromProperties<K, T> {
const Acc = {} as Record<PropertyKey, TSchema>
for(const K2 of globalThis.Object.getOwnPropertyNames(T)) Acc[K2] = FromSchemaType(K, T[K2])
return Acc as never
}
// ------------------------------------------------------------------
// FromMappedPropertyKey
// ------------------------------------------------------------------
// prettier-ignore
type FromSchemaType<K extends PropertyKey, T extends TSchema> = (
// unevaluated modifier types
T extends TReadonly<infer S extends TSchema> ? TReadonly<FromSchemaType<K, S>> :
T extends TOptional<infer S extends TSchema> ? TOptional<FromSchemaType<K, S>> :
// unevaluated mapped types
T extends TMappedResult<infer P extends TProperties> ? TFromMappedResult<K, P> :
T extends TMappedKey<infer P extends PropertyKey[]> ? TFromMappedKey<K, P> :
// unevaluated types
T extends TConstructor<infer S extends TSchema[], infer R extends TSchema> ? TConstructor<TFromRest<K, S>, FromSchemaType<K, R>> :
T extends TFunction<infer S extends TSchema[], infer R extends TSchema> ? TFunction<TFromRest<K, S>, FromSchemaType<K, R>> :
T extends TAsyncIterator<infer S extends TSchema> ? TAsyncIterator<FromSchemaType<K, S>> :
T extends TIterator<infer S extends TSchema> ? TIterator<FromSchemaType<K, S>> :
T extends TIntersect<infer S extends TSchema[]> ? TIntersect<TFromRest<K, S>> :
// note: special case for enums as these are mapped as union types.
T extends TEnum<infer S extends TEnumRecord> ? TEnum<S> :
T extends TUnion<infer S extends TSchema[]> ? TUnion<TFromRest<K, S>> :
T extends TTuple<infer S extends TSchema[]> ? TTuple<TFromRest<K, S>> :
T extends TObject<infer S extends TProperties> ? TObject<FromProperties<K, S>> :
T extends TArray<infer S extends TSchema> ? TArray<FromSchemaType<K, S>> :
T extends TPromise<infer S extends TSchema> ? TPromise<FromSchemaType<K, S>> :
T
)
// prettier-ignore
function FromSchemaType<K extends PropertyKey, T extends TSchema>(K: K, T: T): FromSchemaType<K, T> {
// required to retain user defined options for mapped type
const options = { ...T }
return (
// unevaluated modifier types
IsOptional(T) ? Optional(FromSchemaType(K, Discard(T, [OptionalKind]) as TSchema)) :
IsReadonly(T) ? Readonly(FromSchemaType(K, Discard(T, [ReadonlyKind]) as TSchema)) :
// unevaluated mapped types
IsMappedResult(T) ? FromMappedResult(K, T.properties) :
IsMappedKey(T) ? FromMappedKey(K, T.keys) :
// unevaluated types
IsConstructor(T) ? Constructor(FromRest(K, T.parameters), FromSchemaType(K, T.returns), options) :
IsFunction(T) ? FunctionType(FromRest(K, T.parameters), FromSchemaType(K, T.returns), options) :
IsAsyncIterator(T) ? AsyncIterator(FromSchemaType(K, T.items), options) :
IsIterator(T) ? Iterator(FromSchemaType(K, T.items), options) :
IsIntersect(T) ? Intersect(FromRest(K, T.allOf), options) :
IsUnion(T) ? Union(FromRest(K, T.anyOf), options) :
IsTuple(T) ? Tuple(FromRest(K, T.items ?? []), options) :
IsObject(T) ? Object(FromProperties(K, T.properties), options) :
IsArray(T) ? Array(FromSchemaType(K, T.items), options) :
IsPromise(T) ? Promise(FromSchemaType(K, T.item), options) :
T
) as never
}
// ------------------------------------------------------------------
// MappedFunctionReturnType
// ------------------------------------------------------------------
// prettier-ignore
export type TMappedFunctionReturnType<K extends PropertyKey[], T extends TSchema, Acc extends TProperties = {}> = (
K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
? TMappedFunctionReturnType<R, T, Acc & { [_ in L]: FromSchemaType<L, T> }>
: Acc
)
// prettier-ignore
export function MappedFunctionReturnType<K extends PropertyKey[], T extends TSchema>(K: [...K], T: T): TMappedFunctionReturnType<K, T> {
const Acc = {} as Record<PropertyKey, TSchema>
for(const L of K) Acc[L] = FromSchemaType(L, T)
return Acc as never
}
// ------------------------------------------------------------------
// TMappedFunction
// ------------------------------------------------------------------
// prettier-ignore
export type TMappedFunction<K extends PropertyKey[], I = TMappedKey<K>> = (T: I) => TSchema
// ------------------------------------------------------------------
// TMapped
// ------------------------------------------------------------------
// prettier-ignore
export type TMapped<
K extends PropertyKey[],
F extends TMappedFunction<K>,
R extends TProperties = Evaluate<TMappedFunctionReturnType<K, ReturnType<F>>>,
> = Ensure<TObject<R>>
/** `[Json]` Creates a Mapped object type */
export function Mapped<K extends TSchema, I extends PropertyKey[] = TIndexPropertyKeys<K>, F extends TMappedFunction<I> = TMappedFunction<I>, R extends TMapped<I, F> = TMapped<I, F>>(key: K, map: F, options?: ObjectOptions): R
/** `[Json]` Creates a Mapped object type */
export function Mapped<K extends PropertyKey[], F extends TMappedFunction<K> = TMappedFunction<K>, R extends TMapped<K, F> = TMapped<K, F>>(key: [...K], map: F, options?: ObjectOptions): R
/** `[Json]` Creates a Mapped object type */
export function Mapped(key: any, map: Function, options?: ObjectOptions) {
const K = IsSchema(key) ? IndexPropertyKeys(key) : (key as PropertyKey[])
const RT = map({ [Kind]: 'MappedKey', keys: K } as TMappedKey)
const R = MappedFunctionReturnType(K, RT)
return Object(R, options)
}