@oh-my-pi/omptype 17.2.6 → 17.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/infer.ts CHANGED
@@ -1,4 +1,4 @@
1
- /** Type-level output inference for the definition forms accepted by omptype. */
1
+ /** Type-level input and output inference for definitions accepted by omptype. */
2
2
 
3
3
  type Whitespace = " " | "\n" | "\r" | "\t";
4
4
 
@@ -6,52 +6,158 @@ type TrimLeft<s extends string> = s extends `${Whitespace}${infer rest}` ? TrimL
6
6
  type TrimRight<s extends string> = s extends `${infer rest}${Whitespace}` ? TrimRight<rest> : s;
7
7
  type Trim<s extends string> = TrimLeft<TrimRight<s>>;
8
8
 
9
- type InferPrimitive<s extends string> = s extends "string" | "string.url"
10
- ? string
11
- : s extends "number" | "number.integer"
12
- ? number
13
- : s extends "boolean"
14
- ? boolean
15
- : s extends "null"
16
- ? null
17
- : s extends "undefined"
18
- ? undefined
19
- : s extends "unknown"
20
- ? unknown
21
- : s extends "object"
22
- ? object
23
- : s extends "bigint"
9
+ /**
10
+ * Flat keyword lookup. An indexed access is one instantiation level, unlike a
11
+ * nested conditional chain — this sits under every string-DSL property, so its
12
+ * depth is multiplied by every layer of object nesting above it.
13
+ * `never` is intentionally absent: the parser rejects it and the fallback in
14
+ * `InferMember` treats a missing entry as "not a primitive".
15
+ */
16
+ type ArkAny = ReturnType<typeof JSON.parse>;
17
+
18
+ interface PrimitiveMap {
19
+ string: string;
20
+ "string.url": string;
21
+ number: number;
22
+ "number.integer": number;
23
+ "number.epoch": number;
24
+ "number.safe": number;
25
+ "number.NaN": number;
26
+ "number.Infinity": number;
27
+ "number.NegativeInfinity": number;
28
+ boolean: boolean;
29
+ null: null;
30
+ undefined: undefined;
31
+ unknown: unknown;
32
+ "unknown.any": ArkAny;
33
+ any: unknown;
34
+ object: object;
35
+ bigint: bigint;
36
+ symbol: symbol;
37
+ Key: PropertyKey;
38
+ Date: Date;
39
+ Array: unknown[];
40
+ // biome-ignore lint/complexity/noBannedTypes: built-in Function keyword
41
+ Function: Function;
42
+ RegExp: RegExp;
43
+ File: File;
44
+ Error: Error;
45
+ Set: Set<unknown>;
46
+ Map: Map<unknown, unknown>;
47
+ WeakSet: WeakSet<WeakKey>;
48
+ WeakMap: WeakMap<WeakKey, unknown>;
49
+ Promise: Promise<unknown>;
50
+ FormData: FormData;
51
+ "object.json": unknown;
52
+ true: true;
53
+ false: false;
54
+ }
55
+
56
+ type Merge<left, right> = left extends object
57
+ ? right extends object
58
+ ? Omit<left, keyof right> & right
59
+ : never
60
+ : never;
61
+
62
+ type InferUtility<s extends string> = s extends `Record<${string},${infer value}>`
63
+ ? Record<string, InferString<value>>
64
+ : s extends `Array<${infer element}>` | `Array.liftFrom<${infer element}>`
65
+ ? InferString<element>[]
66
+ : s extends `Partial<${infer value}>`
67
+ ? Partial<InferString<value>>
68
+ : s extends `Required<${infer value}>`
69
+ ? Required<InferString<value>>
70
+ : s extends `Pick<${infer value},${infer keys}>`
71
+ ? Pick<InferString<value>, Extract<InferString<keys>, keyof InferString<value>>>
72
+ : s extends `Omit<${infer value},${infer keys}>`
73
+ ? Omit<InferString<value>, Extract<InferString<keys>, keyof InferString<value>>>
74
+ : s extends `Merge<${infer left},${infer right}>`
75
+ ? Merge<InferString<left>, InferString<right>>
76
+ : never;
77
+
78
+ /** Output types of morph (`.parse`) keywords; `never` when `s` is not one. */
79
+ type InferParse<s extends string> = s extends
80
+ | "string.numeric.parse"
81
+ | "string.integer.parse"
82
+ | "parse.number"
83
+ | "parse.integer"
84
+ ? number
85
+ : s extends "string.date.parse" | "string.date.iso.parse" | "string.date.epoch.parse" | "parse.date"
86
+ ? Date
87
+ : s extends "string.url.parse" | "parse.url"
88
+ ? URL
89
+ : s extends "string.json.parse" | "parse.json"
90
+ ? unknown
91
+ : s extends "object.json.stringify"
92
+ ? string
93
+ : s extends "FormData.parse"
94
+ ? Record<string, Bun.FormDataEntryValue | Bun.FormDataEntryValue[]>
95
+ : s extends "parse.boolean"
96
+ ? boolean
97
+ : s extends "parse.bigint"
24
98
  ? bigint
25
- : s extends "true"
26
- ? true
27
- : s extends "false"
28
- ? false
29
- : never;
99
+ : never;
30
100
 
31
- type InferMember<member extends string> =
32
- Trim<member> extends infer s extends string
33
- ? s extends `${infer element}[]`
34
- ? InferMember<element>[]
35
- : s extends `'${infer literal}'` | `"${infer literal}"`
36
- ? literal
37
- : s extends `${infer literal extends number}`
38
- ? literal
39
- : InferPrimitive<s> extends infer primitive
40
- ? [primitive] extends [never]
41
- ? s extends `${string}string${string}`
42
- ? string
43
- : s extends `${string}number${string}`
44
- ? number
45
- : unknown
46
- : primitive
47
- : unknown
48
- : unknown;
101
+ /**
102
+ * Member inference as a flat false-branch chain: TypeScript tail-evaluates
103
+ * chained conditionals in the false position, so this stays at constant
104
+ * instantiation depth where the previous `extends infer` ladder nested every
105
+ * fallback inside a true branch and accumulated depth per step.
106
+ */
107
+ type InferMember<member extends string> = Trim<member> extends infer s extends string ? InferMemberTrimmed<s> : unknown;
108
+
109
+ type InferMemberTrimmed<s extends string> = s extends `(${infer inner})`
110
+ ? InferString<inner>
111
+ : s extends `${infer element}[]`
112
+ ? InferMember<element>[]
113
+ : s extends `'${infer literal}'` | `"${infer literal}"`
114
+ ? literal
115
+ : s extends `d'${string}'` | `d"${string}"`
116
+ ? Date
117
+ : s extends `\`${string}\``
118
+ ? string
119
+ : s extends `/${string}/${string}` | `/${string}/`
120
+ ? string
121
+ : s extends `${infer literal extends number}`
122
+ ? literal
123
+ : s extends keyof PrimitiveMap
124
+ ? PrimitiveMap[s]
125
+ : [InferParse<s>] extends [never]
126
+ ? InferUtility<s> extends infer utility
127
+ ? [utility] extends [never]
128
+ ? InferMemberFallback<s>
129
+ : utility
130
+ : unknown
131
+ : InferParse<s>;
49
132
 
50
- /** Split top-level unions without distributing over the accumulated members. */
133
+ type InferMemberFallback<s extends string> = s extends `string.${string}`
134
+ ? string
135
+ : s extends `${string}Date${string}`
136
+ ? Date
137
+ : s extends `${string}string${string}`
138
+ ? string
139
+ : s extends `${string}number${string}`
140
+ ? number
141
+ : unknown;
142
+
143
+ /** Split unions without distributing over the accumulated members. */
51
144
  type InferUnion<s extends string, result = never> = s extends `${infer head}|${infer tail}`
52
145
  ? InferUnion<tail, result | InferMember<head>>
53
146
  : result | InferMember<s>;
54
147
 
148
+ /** Input side of one union member: morph keywords accept their source type. */
149
+ type InferMemberIn<member extends string> =
150
+ Trim<member> extends infer s extends string
151
+ ? s extends `${string}.parse` | `parse.${string}`
152
+ ? string
153
+ : InferMemberTrimmed<s>
154
+ : unknown;
155
+
156
+ /** Split unions on the input side without distributing over accumulated members. */
157
+ type InferUnionIn<s extends string, result = never> = s extends `${infer head}|${infer tail}`
158
+ ? InferUnionIn<tail, result | InferMemberIn<head>>
159
+ : result | InferMemberIn<s>;
160
+
55
161
  type HasInlineDefault<s extends string> = s extends `${string}=${string}`
56
162
  ? s extends `${string}<${string}` | `${string}>${string}`
57
163
  ? false
@@ -61,100 +167,276 @@ type HasInlineDefault<s extends string> = s extends `${string}=${string}`
61
167
  type WithoutInlineDefault<s extends string> =
62
168
  HasInlineDefault<s> extends true ? (s extends `${infer base}=${string}` ? Trim<base> : s) : s;
63
169
 
64
- /** String-DSL-only inference; safe inside recursive interfaces (never probes `Type`). */
170
+ type InferStringOutput<s extends string> = InferUnion<s>;
171
+
172
+ /** String-DSL output inference. */
65
173
  export type InferString<s extends string> =
66
174
  WithoutInlineDefault<Trim<s>> extends infer trimmed extends string
67
175
  ? trimmed extends `${infer base}?`
68
176
  ? InferString<base>
69
177
  : trimmed extends `(${infer inner})[]`
70
178
  ? InferUnion<inner>[]
71
- : InferUnion<trimmed>
179
+ : InferStringOutput<trimmed>
180
+ : unknown;
181
+
182
+ /** String-DSL input inference, preserving the source side of morph keywords. */
183
+ export type InferStringIn<s extends string> =
184
+ WithoutInlineDefault<Trim<s>> extends infer trimmed extends string
185
+ ? trimmed extends `${infer base}?`
186
+ ? InferStringIn<base>
187
+ : trimmed extends `(${infer inner})[]`
188
+ ? InferUnionIn<inner>[]
189
+ : InferUnionIn<trimmed>
72
190
  : unknown;
73
191
 
74
192
  type HasDefault<def> = def extends string
75
193
  ? HasInlineDefault<def>
76
- : def extends { readonly hasDefault: true }
194
+ : def extends readonly [unknown, "=", unknown]
77
195
  ? true
78
- : false;
196
+ : def extends { readonly hasDefault: true }
197
+ ? true
198
+ : false;
79
199
 
80
- type DefinitionKeys<def extends object> = Exclude<keyof def, "+" | "[string]">;
200
+ type DefinitionKeys<def extends object> = Exclude<keyof def, "+" | "[string]" | "...">;
81
201
 
82
- /** Optional when the key carries a `?` suffix or the string value does (`limit: "number?"`). */
83
202
  type IsOptionalProp<key, def> = key extends `${string}?`
84
203
  ? true
85
204
  : def extends string
86
205
  ? Trim<def> extends `${string}?`
87
206
  ? true
88
207
  : false
89
- : false;
208
+ : def extends readonly [unknown, "?"]
209
+ ? true
210
+ : false;
90
211
 
91
212
  type PropName<key extends PropertyKey> = key extends `${infer name}?` ? name : key;
213
+ type UnwrapProperty<def> = def extends readonly [infer value, "?" | "=", ...unknown[]] ? value : def;
214
+ type Simplify<t> = { [key in keyof t]: t[key] };
92
215
 
93
- type RequiredProperties<def extends object> = {
216
+ type OutputRequired<def extends object> = {
94
217
  -readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true
95
218
  ? HasDefault<def[key]> extends true
96
219
  ? PropName<key>
97
220
  : never
98
- : PropName<key>]-?: InferDef<def[key]>;
221
+ : PropName<key>]-?: InferDef<UnwrapProperty<def[key]>>;
99
222
  };
100
223
 
101
- type OptionalProperties<def extends object> = {
224
+ type OutputOptional<def extends object> = {
102
225
  -readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true
103
226
  ? HasDefault<def[key]> extends true
104
227
  ? never
105
228
  : PropName<key>
106
- : never]?: InferDef<def[key]>;
229
+ : never]?: InferDef<UnwrapProperty<def[key]>>;
107
230
  };
108
231
 
109
- type Simplify<t> = { [key in keyof t]: t[key] };
110
- type PropertiesOf<def extends object> = Simplify<RequiredProperties<def> & OptionalProperties<def>>;
232
+ type InputRequired<def extends object> = {
233
+ -readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true
234
+ ? never
235
+ : HasDefault<def[key]> extends true
236
+ ? never
237
+ : PropName<key>]-?: InferDefIn<UnwrapProperty<def[key]>>;
238
+ };
239
+
240
+ type InputOptional<def extends object> = {
241
+ -readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true
242
+ ? PropName<key>
243
+ : HasDefault<def[key]> extends true
244
+ ? PropName<key>
245
+ : never]?: InferDefIn<UnwrapProperty<def[key]>>;
246
+ };
247
+
248
+ /**
249
+ * CYCLE SAFETY: when a fluent generic method is called on a schema whose def
250
+ * embeds other schemas, TypeScript instantiates `InferDef<def>` while `def`
251
+ * is still generic. `"..." extends keyof def` resolves TRUE under permissive
252
+ * instantiation (`keyof any` contains every literal), so spread/index members
253
+ * cannot be deferred by wrapper conditionals — a bare `InferDef<def["..."]>`
254
+ * member re-enters this expansion and instantiates without bound (TS2589).
255
+ * Interface members only instantiate when resolved, so routing the recursive
256
+ * reference through `DefBox` keeps generic instantiation shallow: it stops at
257
+ * a type reference plus an indexed access instead of expanding `InferDef`.
258
+ */
259
+ interface DefBox<def> {
260
+ readonly out: InferDef<def>;
261
+ readonly in: InferDefIn<def>;
262
+ }
263
+
264
+ type OutputSpread<def extends object> = "..." extends keyof def ? DefBox<def["..."]>["out"] : unknown;
265
+ type InputSpread<def extends object> = "..." extends keyof def ? DefBox<def["..."]>["in"] : unknown;
266
+ type OutputIndex<def extends object> = "[string]" extends keyof def
267
+ ? Record<string, DefBox<def["[string]"]>["out"]>
268
+ : unknown;
269
+ type InputIndex<def extends object> = "[string]" extends keyof def
270
+ ? Record<string, DefBox<def["[string]"]>["in"]>
271
+ : unknown;
111
272
 
112
273
  type InferObject<def extends object> = "[string]" extends keyof def
113
274
  ? [DefinitionKeys<def>] extends [never]
114
- ? Record<string, InferDef<def["[string]"]>>
115
- : PropertiesOf<def> & Record<string, InferDef<def["[string]"]>>
116
- : PropertiesOf<def>;
275
+ ? Record<string, DefBox<def["[string]"]>["out"]>
276
+ : Simplify<OutputRequired<def> & OutputOptional<def> & OutputSpread<def>> & OutputIndex<def>
277
+ : Simplify<OutputRequired<def> & OutputOptional<def> & OutputSpread<def>>;
117
278
 
118
- /** Object-literal inference for fluent composition without re-inspecting the containing `Type`. */
279
+ type InferObjectIn<def extends object> = "[string]" extends keyof def
280
+ ? [DefinitionKeys<def>] extends [never]
281
+ ? Record<string, DefBox<def["[string]"]>["in"]>
282
+ : Simplify<InputRequired<def> & InputOptional<def> & InputSpread<def>> & InputIndex<def>
283
+ : Simplify<InputRequired<def> & InputOptional<def> & InputSpread<def>>;
284
+
285
+ /** Object-literal inference used by fluent composition overloads. */
119
286
  export type InferObjectDef<def extends object> = InferObject<def>;
120
287
 
121
- type InferLiteralDef<def> = def extends string
122
- ? InferString<def>
123
- : def extends object
124
- ? InferObjectLiteral<def>
125
- : unknown;
288
+ type InferLiteralDef<def> = def extends { readonly infer: infer output }
289
+ ? output
290
+ : def extends string
291
+ ? InferString<def>
292
+ : def extends object
293
+ ? InferObjectLiteral<def>
294
+ : unknown;
295
+
296
+ type InferLiteralDefIn<def> = def extends { readonly inferIn: infer input }
297
+ ? input
298
+ : def extends string
299
+ ? InferStringIn<def>
300
+ : def extends object
301
+ ? InferObjectLiteralIn<def>
302
+ : unknown;
126
303
 
127
- type LiteralRequiredProperties<def extends object> = {
304
+ type LiteralRequired<def extends object> = {
128
305
  -readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true
129
306
  ? HasDefault<def[key]> extends true
130
307
  ? PropName<key>
131
308
  : never
132
- : PropName<key>]-?: InferLiteralDef<def[key]>;
309
+ : PropName<key>]-?: InferLiteralDef<UnwrapProperty<def[key]>>;
133
310
  };
134
311
 
135
- type LiteralOptionalProperties<def extends object> = {
312
+ type LiteralOptional<def extends object> = {
136
313
  -readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true
137
314
  ? HasDefault<def[key]> extends true
138
315
  ? never
139
316
  : PropName<key>
140
- : never]?: InferLiteralDef<def[key]>;
317
+ : never]?: InferLiteralDef<UnwrapProperty<def[key]>>;
141
318
  };
142
319
 
143
- /** Object-literal-only inference for fluent branches; never inspects embedded schema types. */
144
- export type InferObjectLiteral<def extends object> = Simplify<
145
- LiteralRequiredProperties<def> & LiteralOptionalProperties<def>
146
- >;
320
+ type LiteralRequiredIn<def extends object> = {
321
+ -readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true
322
+ ? never
323
+ : HasDefault<def[key]> extends true
324
+ ? never
325
+ : PropName<key>]-?: InferLiteralDefIn<UnwrapProperty<def[key]>>;
326
+ };
327
+
328
+ type LiteralOptionalIn<def extends object> = {
329
+ -readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true
330
+ ? PropName<key>
331
+ : HasDefault<def[key]> extends true
332
+ ? PropName<key>
333
+ : never]?: InferLiteralDefIn<UnwrapProperty<def[key]>>;
334
+ };
335
+
336
+ /** Object-literal inference that unwraps embedded schema values one level deep. */
337
+ export type InferObjectLiteral<def extends object> = Simplify<LiteralRequired<def> & LiteralOptional<def>>;
338
+
339
+ /** Input-side object-literal inference (embedded schemas contribute `inferIn`). */
340
+ export type InferObjectLiteralIn<def extends object> = Simplify<LiteralRequiredIn<def> & LiteralOptionalIn<def>>;
341
+
342
+ type InstanceOf<ctor> = ctor extends abstract new (...args: never[]) => infer instance ? instance : never;
343
+ type SpreadOutput<def> = InferDef<def> extends readonly (infer element)[] ? element[] : never[];
344
+ type SpreadInput<def> = InferDefIn<def> extends readonly (infer element)[] ? element[] : never[];
345
+
346
+ type InferTupleOutput<defs extends readonly unknown[], result extends unknown[] = []> = defs extends readonly []
347
+ ? result
348
+ : defs extends readonly ["...", infer spread, ...infer rest]
349
+ ? [...result, ...SpreadOutput<spread>, ...InferTupleOutput<rest>]
350
+ : defs extends readonly [infer head, ...infer rest]
351
+ ? head extends readonly [infer value, "?"]
352
+ ? InferTupleOutput<rest, [...result, InferDef<value>?]>
353
+ : head extends readonly [infer value, "=", unknown]
354
+ ? InferTupleOutput<rest, [...result, InferDef<value>]>
355
+ : InferTupleOutput<rest, [...result, InferDef<head>]>
356
+ : result;
357
+
358
+ type InferTupleInput<defs extends readonly unknown[], result extends unknown[] = []> = defs extends readonly []
359
+ ? result
360
+ : defs extends readonly ["...", infer spread, ...infer rest]
361
+ ? [...result, ...SpreadInput<spread>, ...InferTupleInput<rest>]
362
+ : defs extends readonly [infer head, ...infer rest]
363
+ ? head extends readonly [infer value, "?" | "=", ...unknown[]]
364
+ ? InferTupleInput<rest, [...result, InferDefIn<value>?]>
365
+ : InferTupleInput<rest, [...result, InferDefIn<head>]>
366
+ : result;
367
+
368
+ /**
369
+ * True only for `any` (`1 & any` is `any`; `0 extends any` holds). During
370
+ * relation checking TypeScript instantiates these aliases permissively with
371
+ * every type parameter replaced by `any`, and under `any` the object branch
372
+ * recurses forever (`any["..."]` is `any` again). Cutting `any` off up front
373
+ * makes permissive instantiation terminate immediately, mirroring ArkType's
374
+ * `anyOrNever` guards.
375
+ */
376
+ type IsAny<def> = 0 extends 1 & def ? true : false;
147
377
 
148
378
  /** Infer the validated output type produced by a definition. */
149
- export type InferDef<def = unknown> = def extends { infer: infer output }
150
- ? output
151
- : def extends string
152
- ? InferString<def>
153
- : def extends readonly [infer element, "[]"]
154
- ? InferDef<element>[]
155
- : def extends object
156
- ? InferObject<def>
157
- : unknown;
379
+ export type InferDef<def = unknown> =
380
+ IsAny<def> extends true
381
+ ? unknown
382
+ : def extends { readonly infer: infer output }
383
+ ? output
384
+ : def extends string
385
+ ? InferString<def>
386
+ : def extends RegExp
387
+ ? string
388
+ : def extends readonly [infer element, "[]"]
389
+ ? InferDef<element>[]
390
+ : def extends readonly [infer left, "|", infer right]
391
+ ? InferDef<left> | InferDef<right>
392
+ : def extends readonly [infer left, "&", infer right]
393
+ ? InferDef<left> & InferDef<right>
394
+ : def extends readonly [unknown, "=>", (...args: never[]) => infer output]
395
+ ? output
396
+ : def extends readonly [unknown, "|>", infer output]
397
+ ? InferDef<output>
398
+ : def extends readonly [infer base, ":", unknown] | readonly [infer base, "@", unknown]
399
+ ? InferDef<base>
400
+ : def extends readonly ["keyof", infer base]
401
+ ? keyof InferDef<base>
402
+ : def extends readonly ["instanceof", ...infer constructors]
403
+ ? InstanceOf<constructors[number]>
404
+ : def extends readonly ["===", ...infer values]
405
+ ? values[number]
406
+ : def extends readonly unknown[]
407
+ ? InferTupleOutput<def>
408
+ : def extends object
409
+ ? InferObject<def>
410
+ : unknown;
158
411
 
159
- /** Input-side defaults and morphs are not distinguished yet. */
160
- export type InferDefIn<def = unknown> = InferDef<def>;
412
+ /** Infer values accepted before defaults and morphs are applied. */
413
+ export type InferDefIn<def = unknown> =
414
+ IsAny<def> extends true
415
+ ? unknown
416
+ : def extends { readonly inferIn: infer input }
417
+ ? input
418
+ : def extends string
419
+ ? InferStringIn<def>
420
+ : def extends RegExp
421
+ ? string
422
+ : def extends readonly [infer element, "[]"]
423
+ ? InferDefIn<element>[]
424
+ : def extends readonly [infer left, "|", infer right]
425
+ ? InferDefIn<left> | InferDefIn<right>
426
+ : def extends readonly [infer left, "&", infer right]
427
+ ? InferDefIn<left> & InferDefIn<right>
428
+ : def extends readonly [infer input, "=>", unknown] | readonly [infer input, "|>", unknown]
429
+ ? InferDefIn<input>
430
+ : def extends readonly [infer base, ":", unknown] | readonly [infer base, "@", unknown]
431
+ ? InferDefIn<base>
432
+ : def extends readonly ["keyof", infer base]
433
+ ? keyof InferDefIn<base>
434
+ : def extends readonly ["instanceof", ...infer constructors]
435
+ ? InstanceOf<constructors[number]>
436
+ : def extends readonly ["===", ...infer values]
437
+ ? values[number]
438
+ : def extends readonly unknown[]
439
+ ? InferTupleInput<def>
440
+ : def extends object
441
+ ? InferObjectIn<def>
442
+ : unknown;