-
-
Notifications
You must be signed in to change notification settings - Fork 159
Expand file tree
/
Copy pathobjectKinds.ts
More file actions
305 lines (265 loc) · 7.97 KB
/
Copy pathobjectKinds.ts
File metadata and controls
305 lines (265 loc) · 7.97 KB
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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
import type { DescribeOptions } from "./describe.ts"
import { type domainDescriptions, domainOf } from "./domain.ts"
import type { Fn } from "./functions.ts"
import type { satisfy } from "./generics.ts"
import { isKeyOf } from "./records.ts"
// ECMAScript Objects
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
export const ecmascriptConstructors = {
Array,
Boolean,
Date,
Error,
Function,
Map,
Number,
Promise,
RegExp,
Set,
String,
WeakMap,
WeakSet
}
export type ecmascriptConstructors = typeof ecmascriptConstructors
// we have to narrow instantiateConstructors here since a lot of the builtin defaults use `any`
export type EcmascriptObjects = satisfy<
instantiateConstructors<keyof ecmascriptConstructors>,
{
Array: Array<unknown>
Boolean: Boolean
Date: Date
Error: Error
Function: Function
Map: Map<unknown, unknown>
Number: Number
RegExp: RegExp
Set: Set<unknown>
String: String
WeakMap: WeakMap<object, unknown>
WeakSet: WeakSet<object>
Promise: Promise<unknown>
}
>
/** Node18 */
export const FileConstructor = globalThis.File ?? Blob
// need to type these explicitly due to a resolution issue
export type platformConstructors = {
ArrayBuffer: ArrayBufferConstructor
Blob: typeof Blob
File: typeof File
FormData: typeof FormData
Headers: typeof Headers
Request: typeof Request
Response: typeof Response
URL: typeof URL
}
// Platform APIs
// See https://developer.mozilla.org/en-US/docs/Web/API
// Must be implemented in Node etc. as well as the browser to include here
export const platformConstructors: platformConstructors = {
ArrayBuffer,
Blob,
File: FileConstructor,
FormData,
Headers,
Request,
Response,
URL
}
export type PlatformObjects = instantiateConstructors<
keyof platformConstructors
>
export const typedArrayConstructors = {
Int8Array,
Uint8Array,
Uint8ClampedArray,
Int16Array,
Uint16Array,
Int32Array,
Uint32Array,
Float32Array,
Float64Array,
BigInt64Array,
BigUint64Array
}
export type typedArrayConstructors = typeof typedArrayConstructors
export type TypedArrayObjects = instantiateConstructors<
keyof typedArrayConstructors
>
// Built-in object constructors based on a subset of:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
export const builtinConstructors = {
...ecmascriptConstructors,
...platformConstructors,
...typedArrayConstructors,
String,
Number,
Boolean
}
export type builtinConstructors = typeof builtinConstructors
export type BuiltinObjectKind = keyof builtinConstructors
export type GlobalName = keyof typeof globalThis
type instantiateConstructors<kind extends BuiltinObjectKind> = {
// one of these conditions will always be true internally, but they prevent
// failed resolutions from being inferred as any if TS is configured
// in such a way that they are unavailable:
// https://github.com/arktypeio/arktype/issues/1246
[k in kind]: k extends GlobalName ? InstanceType<(typeof globalThis)[k]>
: `${k}Constructor` extends GlobalName ?
InstanceType<(typeof globalThis)[`${k}Constructor`]>
: never
}
export type BuiltinObjects = instantiateConstructors<BuiltinObjectKind>
export type objectKindOf<data extends object> =
object extends data ? keyof builtinConstructors | undefined
: data extends Fn ? "Function"
: instantiableObjectKind<data> extends never ? undefined
: instantiableObjectKind<data>
export type describeObject<
o extends object,
opts extends DescribeOptions = {}
> =
objectKindOf<o> extends string ?
[opts["includeArticles"]] extends [true] ?
objectKindDescriptions[objectKindOf<o>]
: objectKindOf<o>
: [opts["includeArticles"]] extends [true] ? domainDescriptions["object"]
: "object"
type instantiableObjectKind<data extends object> = {
[kind in keyof builtinConstructors]: data extends (
InstanceType<builtinConstructors[kind]>
) ?
kind
: never
}[keyof builtinConstructors]
export const objectKindOf = <data extends object>(
data: data
): objectKindOf<data> | undefined => {
// Realm-safe: cross-window/vm arrays fail `instanceof Array` but pass
// Array.isArray (https://github.com/arktypeio/arktype/issues/1597).
if (Array.isArray(data)) return "Array" as never
let prototype: Partial<Object> | null = Object.getPrototypeOf(data)
while (
prototype?.constructor &&
(!isKeyOf(prototype.constructor.name, builtinConstructors) ||
!(data instanceof builtinConstructors[prototype.constructor.name]))
)
prototype = Object.getPrototypeOf(prototype)
const name = prototype?.constructor?.name
if (name === undefined || name === "Object") return undefined
return name as never
}
export const objectKindOrDomainOf = <data>(
data: data
): (objectKindOf<data & object> & {}) | domainOf<data> =>
(typeof data === "object" && data !== null ?
(objectKindOf(data) ?? "object")
: domainOf(data)) as never
export type objectKindOrDomainOf<data> =
data extends object ?
objectKindOf<data> extends undefined ?
"object"
: objectKindOf<data>
: domainOf<data>
export const hasObjectKind = <kind extends keyof builtinConstructors>(
data: object,
kind: kind
): data is InstanceType<builtinConstructors[kind]> =>
objectKindOf(data) === (kind as never)
export const isArray: (data: unknown) => data is readonly unknown[] =
Array.isArray
export const ecmascriptDescriptions = {
Array: "an array",
Function: "a function",
Date: "a Date",
RegExp: "a RegExp",
Error: "an Error",
Map: "a Map",
Set: "a Set",
String: "a String object",
Number: "a Number object",
Boolean: "a Boolean object",
Promise: "a Promise",
WeakMap: "a WeakMap",
WeakSet: "a WeakSet"
} as const satisfies Record<keyof EcmascriptObjects, string>
export const platformDescriptions = {
ArrayBuffer: "an ArrayBuffer instance",
Blob: "a Blob instance",
File: "a File instance",
FormData: "a FormData instance",
Headers: "a Headers instance",
Request: "a Request instance",
Response: "a Response instance",
URL: "a URL instance"
}
export const typedArrayDescriptions = {
Int8Array: "an Int8Array",
Uint8Array: "a Uint8Array",
Uint8ClampedArray: "a Uint8ClampedArray",
Int16Array: "an Int16Array",
Uint16Array: "a Uint16Array",
Int32Array: "an Int32Array",
Uint32Array: "a Uint32Array",
Float32Array: "a Float32Array",
Float64Array: "a Float64Array",
BigInt64Array: "a BigInt64Array",
BigUint64Array: "a BigUint64Array"
} as const satisfies Record<keyof typedArrayConstructors, string>
/** Each defaultObjectKind's completion for the phrase "must be _____" */
export const objectKindDescriptions = {
...ecmascriptDescriptions,
...platformDescriptions,
...typedArrayDescriptions
} as const satisfies Record<BuiltinObjectKind, string>
export type objectKindDescriptions = typeof objectKindDescriptions
/**
* this will only return an object kind if it's the root constructor
* example TypeError would return null not 'Error'
**/
export const getBuiltinNameOfConstructor = (
ctor: Function
): BuiltinObjectKind | null => {
const constructorName: string | null = Object(ctor).name ?? null
return (
constructorName &&
isKeyOf(constructorName, builtinConstructors) &&
builtinConstructors[constructorName] === ctor
) ?
constructorName
: null
}
export type Constructor<instance = {}> = abstract new (
...args: never[]
) => instance
export type instanceOf<constructor> =
constructor extends Constructor<infer instance> ? instance : never
/**
* Returns an array of constructors for all ancestors (i.e., prototypes) of a given object.
*/
export const ancestorsOf = (o: object): Function[] => {
let proto = Object.getPrototypeOf(o)
const result: Function[] = []
while (proto !== null) {
result.push(proto.constructor)
proto = Object.getPrototypeOf(proto)
}
return result
}
export type normalizedKeyOf<t> =
keyof t extends infer k ?
k extends number ?
`${k}`
: k
: never
export const constructorExtends = (
ctor: Constructor,
base: Constructor
): boolean => {
let current = ctor.prototype
while (current !== null) {
if (current === base.prototype) return true
current = Object.getPrototypeOf(current)
}
return false
}