@fluidframework/core-interfaces 2.51.0 → 2.52.0
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/CHANGELOG.md +4 -0
- package/dist/exposedInternalUtilityTypes.d.ts +106 -50
- package/dist/exposedInternalUtilityTypes.d.ts.map +1 -1
- package/dist/exposedInternalUtilityTypes.js.map +1 -1
- package/lib/exposedInternalUtilityTypes.d.ts +106 -50
- package/lib/exposedInternalUtilityTypes.d.ts.map +1 -1
- package/lib/exposedInternalUtilityTypes.js.map +1 -1
- package/package.json +4 -4
- package/src/exposedInternalUtilityTypes.ts +234 -130
|
@@ -118,6 +118,11 @@ export namespace InternalUtilityTypes {
|
|
|
118
118
|
* Typically this will be `NonNullJsonObjectWith<TupleToUnion<AllowExactly> | AllowExtensionOf>`.
|
|
119
119
|
*/
|
|
120
120
|
DegenerateNonNullObjectSubstitute: unknown;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Either `RecursionMarker` when filtering after recursion has been replace or `never`
|
|
124
|
+
*/
|
|
125
|
+
RecursionMarkerAllowed: unknown; // Can be RecursionMarker ?
|
|
121
126
|
}
|
|
122
127
|
|
|
123
128
|
/**
|
|
@@ -137,7 +142,12 @@ export namespace InternalUtilityTypes {
|
|
|
137
142
|
[K in Keys]: T extends Record<K, T[K]> ? never : K;
|
|
138
143
|
}[Keys],
|
|
139
144
|
undefined | symbol
|
|
140
|
-
|
|
145
|
+
> extends infer Result
|
|
146
|
+
? // Workaround for TypeScript bug/limitation where an alias for a type
|
|
147
|
+
// is not considered the same type when used as an index. This restores
|
|
148
|
+
// the original `Keys` (`keyof T`) type when there is no filtering.
|
|
149
|
+
IfSameType<Keys, Result, Keys, Extract<Result, string | number>>
|
|
150
|
+
: never;
|
|
141
151
|
|
|
142
152
|
/**
|
|
143
153
|
* Returns non-symbol keys for required properties of an object type.
|
|
@@ -156,7 +166,12 @@ export namespace InternalUtilityTypes {
|
|
|
156
166
|
[K in Keys]: T extends Record<K, T[K]> ? K : never;
|
|
157
167
|
}[Keys],
|
|
158
168
|
undefined | symbol
|
|
159
|
-
|
|
169
|
+
> extends infer Result
|
|
170
|
+
? // Workaround for TypeScript bug/limitation where an alias for a type
|
|
171
|
+
// is not considered the same type when used as an index. This restores
|
|
172
|
+
// the original `Keys` (`keyof T`) type when there is no filtering.
|
|
173
|
+
IfSameType<Keys, Result, Keys, Extract<Result, string | number>>
|
|
174
|
+
: never;
|
|
160
175
|
|
|
161
176
|
/**
|
|
162
177
|
* Returns Result.WhenSomethingDeserializable if T is sometimes at least a
|
|
@@ -259,9 +274,10 @@ export namespace InternalUtilityTypes {
|
|
|
259
274
|
|
|
260
275
|
/**
|
|
261
276
|
* Returns non-symbol keys for defined, (likely) serializable properties of an
|
|
262
|
-
* object type.
|
|
263
|
-
* bigint) and sometimes unsupported (functions) are excluded. An exception to
|
|
264
|
-
* that is when there are supported types in union with just bigint.
|
|
277
|
+
* object type. Literal keys with fully unsupported properties (undefined, symbol,
|
|
278
|
+
* and bigint) and sometimes unsupported (functions) are excluded. An exception to
|
|
279
|
+
* that is when there are supported types in union with just bigint. Indexed keys
|
|
280
|
+
* are only excluded when there are no supported properties.
|
|
265
281
|
*
|
|
266
282
|
* For homomorphic mapping use with `as` to filter. Example:
|
|
267
283
|
* `[K in keyof T as NonSymbolWithDeserializablePropertyOf<T, [], never, K>]: ...`
|
|
@@ -283,7 +299,11 @@ export namespace InternalUtilityTypes {
|
|
|
283
299
|
? IfSameType<
|
|
284
300
|
PossibleTypeLessAllowed,
|
|
285
301
|
unknown,
|
|
286
|
-
/* value might not be supported =>
|
|
302
|
+
/* value might not be supported => check for indexed key */ IfIndexOrBrandedKey<
|
|
303
|
+
K,
|
|
304
|
+
/* indexed => allow K */ K,
|
|
305
|
+
/* literal => exclude K */ never
|
|
306
|
+
>,
|
|
287
307
|
/* extract types that might lead to missing property */ Extract<
|
|
288
308
|
PossibleTypeLessAllowed,
|
|
289
309
|
/* types that might lead to missing property, except `bigint` */
|
|
@@ -298,15 +318,32 @@ export namespace InternalUtilityTypes {
|
|
|
298
318
|
/* exclusively supported types (and maybe `bigint`) or exactly `never` */
|
|
299
319
|
/* => check for `never` */ T[K] extends never ? never : K
|
|
300
320
|
>
|
|
301
|
-
: /* value might not be supported =>
|
|
321
|
+
: /* value might not be supported => check for any supported */ TestDeserializabilityOf<
|
|
322
|
+
T[K],
|
|
323
|
+
OmitExactlyFromTuple<TExactExceptions, unknown>,
|
|
324
|
+
TExtendsException,
|
|
325
|
+
{
|
|
326
|
+
WhenSomethingDeserializable: /* => check for indexed key */ IfIndexOrBrandedKey<
|
|
327
|
+
K,
|
|
328
|
+
/* indexed => allow K */ K,
|
|
329
|
+
/* literal => exclude K */ never
|
|
330
|
+
>;
|
|
331
|
+
WhenNeverDeserializable: /* => exclude K */ never;
|
|
332
|
+
}
|
|
333
|
+
>
|
|
302
334
|
>
|
|
303
335
|
: never;
|
|
304
336
|
}[Keys],
|
|
305
337
|
undefined | symbol
|
|
306
|
-
|
|
338
|
+
> extends infer Result
|
|
339
|
+
? // Workaround for TypeScript bug/limitation where an alias for a type
|
|
340
|
+
// is not considered the same type when used as an index. This restores
|
|
341
|
+
// the original `Keys` (`keyof T`) type when there is no filtering.
|
|
342
|
+
IfSameType<Keys, Result, Keys, Extract<Result, string | number>>
|
|
343
|
+
: never;
|
|
307
344
|
|
|
308
345
|
/**
|
|
309
|
-
* Returns non-symbol keys for partially supported properties of an object type.
|
|
346
|
+
* Returns non-symbol, literal keys for partially supported properties of an object type.
|
|
310
347
|
* Keys with only unsupported properties (undefined, symbol, bigint, and
|
|
311
348
|
* functions without other properties) are excluded.
|
|
312
349
|
*
|
|
@@ -315,35 +352,48 @@ export namespace InternalUtilityTypes {
|
|
|
315
352
|
*
|
|
316
353
|
* @system
|
|
317
354
|
*/
|
|
318
|
-
export type
|
|
355
|
+
export type NonSymbolLiteralWithPossiblyDeserializablePropertyOf<
|
|
319
356
|
T extends object,
|
|
320
357
|
TExactExceptions extends unknown[],
|
|
321
358
|
TExtendsException,
|
|
322
359
|
Keys extends keyof T = keyof T,
|
|
323
360
|
> = Exclude<
|
|
324
361
|
{
|
|
325
|
-
[K in Keys]:
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
362
|
+
[K in Keys]: IfIndexOrBrandedKey<
|
|
363
|
+
K,
|
|
364
|
+
/* indexed => exclude K */ never,
|
|
365
|
+
/* literal => ... */
|
|
366
|
+
/* all possible types that aren't already allowed, with the exception of `unknown` */
|
|
367
|
+
ExcludeExactlyInTuple<
|
|
368
|
+
Exclude<T[K], TExtendsException>,
|
|
369
|
+
OmitExactlyFromTuple<TExactExceptions, unknown>
|
|
370
|
+
> extends infer PossibleTypeLessAllowed
|
|
371
|
+
? Extract<
|
|
372
|
+
IfSameType<PossibleTypeLessAllowed, unknown, undefined, PossibleTypeLessAllowed>,
|
|
373
|
+
/* types that might lead to missing property */
|
|
374
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
375
|
+
undefined | symbol | Function
|
|
376
|
+
> extends never
|
|
377
|
+
? /* exclusively supported types or exactly `never` */ never
|
|
378
|
+
: /* at least some unsupported type => check for any supported */ TestDeserializabilityOf<
|
|
379
|
+
T[K],
|
|
380
|
+
OmitExactlyFromTuple<TExactExceptions, unknown>,
|
|
381
|
+
TExtendsException,
|
|
382
|
+
{
|
|
383
|
+
WhenSomethingDeserializable: K;
|
|
384
|
+
WhenNeverDeserializable: never;
|
|
385
|
+
}
|
|
386
|
+
>
|
|
387
|
+
: never
|
|
388
|
+
>;
|
|
344
389
|
}[Keys],
|
|
345
390
|
undefined | symbol
|
|
346
|
-
|
|
391
|
+
> extends infer Result
|
|
392
|
+
? // Workaround for TypeScript bug/limitation where an alias for a type
|
|
393
|
+
// is not considered the same type when used as an index. This restores
|
|
394
|
+
// the original `Keys` (`keyof T`) type when there is no filtering.
|
|
395
|
+
IfSameType<Keys, Result, Keys, Extract<Result, string | number>>
|
|
396
|
+
: never;
|
|
347
397
|
|
|
348
398
|
/**
|
|
349
399
|
* Filters a type `T` for `undefined` that is not viable in an array (or tuple) that
|
|
@@ -409,7 +459,7 @@ export namespace InternalUtilityTypes {
|
|
|
409
459
|
? /* primitive or replaced types => */ T
|
|
410
460
|
: /* test for exact alternative */ IfExactTypeInTuple<
|
|
411
461
|
T,
|
|
412
|
-
Controls["AllowExactly"],
|
|
462
|
+
[...Controls["AllowExactly"], Controls["RecursionMarkerAllowed"]],
|
|
413
463
|
/* exactly replaced => */ T,
|
|
414
464
|
/* test for known types that become null */ T extends undefined | symbol
|
|
415
465
|
? /* => */ null
|
|
@@ -516,6 +566,47 @@ export namespace InternalUtilityTypes {
|
|
|
516
566
|
/* T is NOT never => */ IfSameType<T, Extract<Union, T>, IfMatch, IfNoMatch>
|
|
517
567
|
>;
|
|
518
568
|
|
|
569
|
+
/**
|
|
570
|
+
* Check for a template literal that has $\{string\} or $\{number\}
|
|
571
|
+
* in the pattern. Just `string` and/or `number` also match.
|
|
572
|
+
*
|
|
573
|
+
* @remarks This works recursively looking at first elements when not
|
|
574
|
+
* `string` or `number`. `first` will just be a single character if
|
|
575
|
+
* not $\{string\} or $\{number\}.
|
|
576
|
+
*
|
|
577
|
+
* @system
|
|
578
|
+
*/
|
|
579
|
+
export type IfVariableStringOrNumber<T, IfVariable, IfLiteral> = `${string}` extends T
|
|
580
|
+
? IfVariable
|
|
581
|
+
: number extends T
|
|
582
|
+
? IfVariable
|
|
583
|
+
: T extends `${infer first}${infer rest}`
|
|
584
|
+
? string extends first
|
|
585
|
+
? IfVariable
|
|
586
|
+
: `${number}` extends first
|
|
587
|
+
? IfVariable
|
|
588
|
+
: IfVariableStringOrNumber<rest, IfVariable, IfLiteral>
|
|
589
|
+
: IfLiteral;
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Essentially a check for a non-fixed number or string OR a branded key.
|
|
593
|
+
*
|
|
594
|
+
* @remarks There is no known mechanism to determine the primitive from a
|
|
595
|
+
* generic tagged (branded) primitive, such as `X$\{string\}` & \{ foo: "bar" \}.
|
|
596
|
+
* There appears to be little use for a branded literal key -- at runtime
|
|
597
|
+
* there is no brand; so, two of the same literal with different brands would
|
|
598
|
+
* collide. Thus any branded key can usually be considered indexed.
|
|
599
|
+
*
|
|
600
|
+
* @system
|
|
601
|
+
*/
|
|
602
|
+
export type IfIndexOrBrandedKey<
|
|
603
|
+
T extends keyof AnyRecord,
|
|
604
|
+
IfIndexOrBranded,
|
|
605
|
+
Otherwise,
|
|
606
|
+
> = T extends object
|
|
607
|
+
? /* branded string or number */ IfIndexOrBranded
|
|
608
|
+
: /* => check for variable */ IfVariableStringOrNumber<T, IfIndexOrBranded, Otherwise>;
|
|
609
|
+
|
|
519
610
|
/**
|
|
520
611
|
* Test for type equality
|
|
521
612
|
*
|
|
@@ -559,7 +650,7 @@ export namespace InternalUtilityTypes {
|
|
|
559
650
|
: T;
|
|
560
651
|
|
|
561
652
|
/**
|
|
562
|
-
* Convenience constraint for any
|
|
653
|
+
* Convenience constraint for any OpaqueJson* type.
|
|
563
654
|
*
|
|
564
655
|
* @remarks
|
|
565
656
|
* Use in extends check: `T extends AnyOpaqueJsonType`
|
|
@@ -825,9 +916,8 @@ export namespace InternalUtilityTypes {
|
|
|
825
916
|
| boolean
|
|
826
917
|
| number
|
|
827
918
|
| string
|
|
828
|
-
// Add in
|
|
919
|
+
// Add in OpaqueJson* types
|
|
829
920
|
| AnyOpaqueJsonType;
|
|
830
|
-
DegenerateSubstitute: Controls["DegenerateSubstitute"];
|
|
831
921
|
},
|
|
832
922
|
"found non-publics",
|
|
833
923
|
"only publics"
|
|
@@ -855,7 +945,7 @@ export namespace InternalUtilityTypes {
|
|
|
855
945
|
: never /* unreachable else for infer */;
|
|
856
946
|
|
|
857
947
|
/**
|
|
858
|
-
* Handle
|
|
948
|
+
* Handle OpaqueJson* types for {@link JsonSerializable}.
|
|
859
949
|
*
|
|
860
950
|
* @remarks
|
|
861
951
|
* {@link OpaqueJsonSerializable} and {@link OpaqueJsonDeserialized} instances
|
|
@@ -891,32 +981,10 @@ export namespace InternalUtilityTypes {
|
|
|
891
981
|
Controls["AllowExactly"],
|
|
892
982
|
Controls["AllowExtensionOf"]
|
|
893
983
|
>
|
|
894
|
-
: "internal error: failed to determine
|
|
984
|
+
: "internal error: failed to determine OpaqueJson* type"
|
|
895
985
|
: never;
|
|
896
986
|
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
897
987
|
|
|
898
|
-
/**
|
|
899
|
-
* Essentially a check for a template literal that has $\{string\} or
|
|
900
|
-
* $\{number\} in the pattern. Just `string` and/or `number` also match.
|
|
901
|
-
*
|
|
902
|
-
* @remarks This works recursively looking at first elements when not
|
|
903
|
-
* `string` or `number`. `first` will just be a single character if
|
|
904
|
-
* not $\{string\} or $\{number\}.
|
|
905
|
-
*
|
|
906
|
-
* @system
|
|
907
|
-
*/
|
|
908
|
-
export type IfIndexKey<T, IfIndex, IfLiteral> = `${string}` extends T
|
|
909
|
-
? IfIndex
|
|
910
|
-
: number extends T
|
|
911
|
-
? IfIndex
|
|
912
|
-
: T extends `${infer first}${infer rest}`
|
|
913
|
-
? string extends first
|
|
914
|
-
? IfIndex
|
|
915
|
-
: `${number}` extends first
|
|
916
|
-
? IfIndex
|
|
917
|
-
: IfIndexKey<rest, IfIndex, IfLiteral>
|
|
918
|
-
: IfLiteral;
|
|
919
|
-
|
|
920
988
|
/**
|
|
921
989
|
* Helper for {@link JsonSerializableFilter} to determine if a property may
|
|
922
990
|
* be `undefined` and selects from options for result.
|
|
@@ -927,7 +995,7 @@ export namespace InternalUtilityTypes {
|
|
|
927
995
|
* @system
|
|
928
996
|
*/
|
|
929
997
|
export type IfPossiblyUndefinedProperty<
|
|
930
|
-
TKey,
|
|
998
|
+
TKey extends keyof AnyRecord,
|
|
931
999
|
TValue,
|
|
932
1000
|
Result extends {
|
|
933
1001
|
IfPossiblyUndefined: unknown;
|
|
@@ -936,7 +1004,7 @@ export namespace InternalUtilityTypes {
|
|
|
936
1004
|
},
|
|
937
1005
|
> = undefined extends TValue
|
|
938
1006
|
? unknown extends TValue
|
|
939
|
-
?
|
|
1007
|
+
? IfIndexOrBrandedKey<TKey, Result["Otherwise"], Result["IfUnknownNonIndexed"]>
|
|
940
1008
|
: Result["IfPossiblyUndefined"]
|
|
941
1009
|
: Result["Otherwise"];
|
|
942
1010
|
|
|
@@ -1003,8 +1071,8 @@ export namespace InternalUtilityTypes {
|
|
|
1003
1071
|
>
|
|
1004
1072
|
: /* test for enum like types */ IfEnumLike<T> extends never
|
|
1005
1073
|
? /* enum or similar simple type (return as-is) => */ T
|
|
1006
|
-
: /* test for
|
|
1007
|
-
? /*
|
|
1074
|
+
: /* test for OpaqueJson* types */ T extends AnyOpaqueJsonType
|
|
1075
|
+
? /* OpaqueJson* type => */ JsonSerializableOpaqueAllowances<
|
|
1008
1076
|
T,
|
|
1009
1077
|
Controls
|
|
1010
1078
|
>
|
|
@@ -1062,28 +1130,26 @@ export namespace InternalUtilityTypes {
|
|
|
1062
1130
|
[RecursionMarkerSymbol]: typeof RecursionMarkerSymbol;
|
|
1063
1131
|
}
|
|
1064
1132
|
|
|
1065
|
-
/**
|
|
1066
|
-
* Recursion limit is the count of `+` that prefix it when string.
|
|
1067
|
-
*
|
|
1068
|
-
* @system
|
|
1069
|
-
*/
|
|
1070
|
-
export type RecursionLimit = `+${string}` | 0;
|
|
1071
|
-
|
|
1072
1133
|
// #region JsonDeserialized implementation
|
|
1073
1134
|
|
|
1074
1135
|
/**
|
|
1075
1136
|
* Outer implementation of {@link JsonDeserialized} handling meta cases
|
|
1076
1137
|
* like recursive types.
|
|
1077
1138
|
*
|
|
1078
|
-
* @
|
|
1079
|
-
*
|
|
1139
|
+
* @remarks
|
|
1140
|
+
* If no modification is needed (`T` is exactly deserializable), `T` will
|
|
1141
|
+
* be the result.
|
|
1142
|
+
*
|
|
1143
|
+
* Upon recursion with `T` that requires modification, `T` is wrapped in
|
|
1144
|
+
* {@link OpaqueJsonDeserialized} to avoid further immediate processing.
|
|
1145
|
+
* Caller will need to unwrap the type to continue processing.
|
|
1080
1146
|
*
|
|
1081
1147
|
* @system
|
|
1082
1148
|
*/
|
|
1083
1149
|
export type JsonDeserializedImpl<
|
|
1084
1150
|
T,
|
|
1085
1151
|
Options extends Partial<FilterControls>,
|
|
1086
|
-
|
|
1152
|
+
TypeUnderRecursion extends boolean = false,
|
|
1087
1153
|
> = /* Build Controls from Options filling in defaults for any missing properties */
|
|
1088
1154
|
{
|
|
1089
1155
|
AllowExactly: Options extends { AllowExactly: unknown[] } ? Options["AllowExactly"] : [];
|
|
@@ -1105,6 +1171,7 @@ export namespace InternalUtilityTypes {
|
|
|
1105
1171
|
: never)
|
|
1106
1172
|
| (Options extends { AllowExtensionOf: unknown } ? Options["AllowExtensionOf"] : never)
|
|
1107
1173
|
>;
|
|
1174
|
+
RecursionMarkerAllowed: never;
|
|
1108
1175
|
} extends infer Controls
|
|
1109
1176
|
? /* Controls should always satisfy DeserializedFilterControls, but Typescript wants a check */
|
|
1110
1177
|
Controls extends DeserializedFilterControls
|
|
@@ -1113,19 +1180,25 @@ export namespace InternalUtilityTypes {
|
|
|
1113
1180
|
: /* infer non-recursive version of T */ ReplaceRecursionWithMarkerAndPreserveAllowances<
|
|
1114
1181
|
T,
|
|
1115
1182
|
RecursionMarker,
|
|
1116
|
-
|
|
1183
|
+
{
|
|
1184
|
+
AllowExactly: Controls["AllowExactly"];
|
|
1185
|
+
AllowExtensionOf:
|
|
1186
|
+
| Controls["AllowExtensionOf"]
|
|
1187
|
+
// Also preserve OpaqueJson* types
|
|
1188
|
+
| AnyOpaqueJsonType;
|
|
1189
|
+
}
|
|
1117
1190
|
> extends infer TNoRecursionAndOnlyPublics
|
|
1118
1191
|
? /* test for no change from filtered type */ IsSameType<
|
|
1119
1192
|
TNoRecursionAndOnlyPublics,
|
|
1120
1193
|
JsonDeserializedFilter<
|
|
1121
1194
|
TNoRecursionAndOnlyPublics,
|
|
1122
1195
|
{
|
|
1123
|
-
AllowExactly:
|
|
1196
|
+
AllowExactly: Controls["AllowExactly"];
|
|
1124
1197
|
AllowExtensionOf: Controls["AllowExtensionOf"];
|
|
1125
1198
|
DegenerateSubstitute: Controls["DegenerateSubstitute"];
|
|
1126
1199
|
DegenerateNonNullObjectSubstitute: Controls["DegenerateNonNullObjectSubstitute"];
|
|
1127
|
-
|
|
1128
|
-
|
|
1200
|
+
RecursionMarkerAllowed: RecursionMarker;
|
|
1201
|
+
}
|
|
1129
1202
|
>
|
|
1130
1203
|
> extends true
|
|
1131
1204
|
? /* same (no filtering needed) => test for non-public
|
|
@@ -1134,58 +1207,85 @@ export namespace InternalUtilityTypes {
|
|
|
1134
1207
|
T,
|
|
1135
1208
|
// Note: no extra allowance is made here for possible branded
|
|
1136
1209
|
// primitives as JsonDeserializedFilter will allow them as
|
|
1137
|
-
// extensions of the primitives. Should there
|
|
1138
|
-
//
|
|
1139
|
-
|
|
1210
|
+
// extensions of the primitives. Should there be a need to
|
|
1211
|
+
// explicitly allow them here, see JsonSerializableImpl's use.
|
|
1212
|
+
{
|
|
1213
|
+
AllowExactly: Controls["AllowExactly"];
|
|
1214
|
+
AllowExtensionOf:
|
|
1215
|
+
| Controls["AllowExtensionOf"]
|
|
1216
|
+
// Add in OpaqueJson* types
|
|
1217
|
+
| AnyOpaqueJsonType;
|
|
1218
|
+
},
|
|
1140
1219
|
"found non-publics",
|
|
1141
1220
|
"only publics"
|
|
1142
1221
|
> extends "found non-publics"
|
|
1143
1222
|
? /* hidden props => apply filtering to avoid retaining
|
|
1144
|
-
exact class except for any classes in allowances =>
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
// that could arise from a reset and being conservative.
|
|
1155
|
-
RecurseLimit
|
|
1156
|
-
>
|
|
1223
|
+
exact class except for any classes in allowances =>
|
|
1224
|
+
test for known recursion */
|
|
1225
|
+
TypeUnderRecursion extends false
|
|
1226
|
+
? /* no known recursion => */ JsonDeserializedFilter<T, Controls>
|
|
1227
|
+
: /* known recursion => use OpaqueJsonDeserialized for later processing */
|
|
1228
|
+
OpaqueJsonDeserialized<
|
|
1229
|
+
T,
|
|
1230
|
+
Controls["AllowExactly"],
|
|
1231
|
+
Controls["AllowExtensionOf"]
|
|
1232
|
+
>
|
|
1157
1233
|
: /* no hidden properties => deserialized T is just T */
|
|
1158
1234
|
T
|
|
1159
|
-
: /* filtering is needed => */
|
|
1235
|
+
: /* filtering is needed => test for known recursion */ TypeUnderRecursion extends false
|
|
1236
|
+
? /* no known recursion => */ JsonDeserializedFilter<T, Controls>
|
|
1237
|
+
: /* known recursion => use OpaqueJsonDeserialized for later processing */
|
|
1238
|
+
OpaqueJsonDeserialized<
|
|
1239
|
+
T,
|
|
1240
|
+
Controls["AllowExactly"],
|
|
1241
|
+
Controls["AllowExtensionOf"]
|
|
1242
|
+
>
|
|
1160
1243
|
: /* unreachable else for infer */ never
|
|
1161
1244
|
: never /* DeserializedFilterControls assert else; should never be reached */
|
|
1162
1245
|
: never /* unreachable else for infer */;
|
|
1163
1246
|
|
|
1164
1247
|
/**
|
|
1165
|
-
* Recurses `T` applying {@link InternalUtilityTypes.JsonDeserializedFilter} up
|
|
1248
|
+
* Recurses `T` applying {@link InternalUtilityTypes.JsonDeserializedFilter} up until
|
|
1249
|
+
* `T` is found to be a match of an ancestor type. At that point `T` is wrapped in
|
|
1250
|
+
* {@link OpaqueJsonDeserialized} to avoid further immediate processing, if
|
|
1251
|
+
* modification is needed. Caller will need to unwrap the type to continue processing.
|
|
1252
|
+
* If no modification is needed, then `T` will be the result.
|
|
1253
|
+
*
|
|
1254
|
+
* @privateRemarks Exact recursion pattern employed should allow shortcut
|
|
1255
|
+
* test to return `OpaqueJsonDeserialized<T>` from here when the exact
|
|
1256
|
+
* recursion match is the first ancestor type in tuple as it must have been
|
|
1257
|
+
* processed and found to need modification.
|
|
1166
1258
|
*
|
|
1167
1259
|
* @system
|
|
1168
1260
|
*/
|
|
1169
1261
|
export type JsonDeserializedRecursion<
|
|
1170
1262
|
T,
|
|
1171
1263
|
Controls extends DeserializedFilterControls,
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1264
|
+
TAncestorTypes extends object[],
|
|
1265
|
+
> = IfExactTypeInTuple<T, TAncestorTypes, true, "no match"> extends true
|
|
1266
|
+
? /* recursion found => reprocess that recursive type directly to avoid
|
|
1267
|
+
any collateral damage from ancestor type that required modification.
|
|
1268
|
+
Further recursion will not happen during processing. Either:
|
|
1269
|
+
- the type requires modification and the `TypeUnderRecursion=true`
|
|
1270
|
+
arg will result in `OpaqueJsonDeserialized<T>` wrapper OR
|
|
1271
|
+
- no change is needed from this point and `T` will result. */
|
|
1272
|
+
JsonDeserializedImpl<T, Controls, true>
|
|
1273
|
+
: T extends object
|
|
1274
|
+
? IfExactTypeInTuple<T, TAncestorTypes, true, "no match"> extends true
|
|
1275
|
+
? JsonDeserializedImpl<T, Controls, true>
|
|
1276
|
+
: /* no recursion yet detected => */ JsonDeserializedFilter<
|
|
1277
|
+
T,
|
|
1278
|
+
Controls,
|
|
1279
|
+
[...TAncestorTypes, T]
|
|
1280
|
+
>
|
|
1281
|
+
: /* not an object (no recursion) => */ JsonDeserializedFilter<
|
|
1180
1282
|
T,
|
|
1181
1283
|
Controls,
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
: Controls["DegenerateSubstitute"]
|
|
1185
|
-
: JsonDeserializedFilter<T, Controls, RecurseLimit, TAncestorTypes | T>;
|
|
1284
|
+
TAncestorTypes
|
|
1285
|
+
>;
|
|
1186
1286
|
|
|
1187
1287
|
/**
|
|
1188
|
-
* Handle
|
|
1288
|
+
* Handle OpaqueJson* types for {@link JsonDeserialized}.
|
|
1189
1289
|
*
|
|
1190
1290
|
* @remarks
|
|
1191
1291
|
* {@link OpaqueJsonSerializable} instances are converted to {@link OpaqueJsonDeserialized}.
|
|
@@ -1210,7 +1310,7 @@ export namespace InternalUtilityTypes {
|
|
|
1210
1310
|
| OpaqueJsonSerializable<infer TData, any, unknown>
|
|
1211
1311
|
| OpaqueJsonDeserialized<infer TData, any, unknown>
|
|
1212
1312
|
? OpaqueJsonDeserialized<TData, Controls["AllowExactly"], Controls["AllowExtensionOf"]>
|
|
1213
|
-
: "internal error: failed to determine
|
|
1313
|
+
: "internal error: failed to determine OpaqueJson* type";
|
|
1214
1314
|
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
1215
1315
|
|
|
1216
1316
|
/**
|
|
@@ -1221,8 +1321,9 @@ export namespace InternalUtilityTypes {
|
|
|
1221
1321
|
export type JsonDeserializedFilter<
|
|
1222
1322
|
T,
|
|
1223
1323
|
Controls extends DeserializedFilterControls,
|
|
1224
|
-
|
|
1225
|
-
|
|
1324
|
+
// Always start with object portion of self as ancestor. Filtering will
|
|
1325
|
+
// not apply past recursion (nested occurrence of this).
|
|
1326
|
+
TAncestorTypes extends object[] = [Extract<T, object>],
|
|
1226
1327
|
> = /* test for 'any' */ boolean extends (T extends never ? true : false)
|
|
1227
1328
|
? /* 'any' => */ Controls["DegenerateSubstitute"]
|
|
1228
1329
|
: /* test for 'unknown' */ unknown extends T
|
|
@@ -1236,7 +1337,7 @@ export namespace InternalUtilityTypes {
|
|
|
1236
1337
|
? /* primitive types or alternate => */ T
|
|
1237
1338
|
: /* test for given exact alternate */ IfExactTypeInTuple<
|
|
1238
1339
|
T,
|
|
1239
|
-
Controls["AllowExactly"],
|
|
1340
|
+
[...Controls["AllowExactly"], Controls["RecursionMarkerAllowed"]],
|
|
1240
1341
|
true,
|
|
1241
1342
|
"not found"
|
|
1242
1343
|
> extends true
|
|
@@ -1254,43 +1355,39 @@ export namespace InternalUtilityTypes {
|
|
|
1254
1355
|
[K in keyof T]: JsonForDeserializedArrayItem<
|
|
1255
1356
|
T[K],
|
|
1256
1357
|
Controls,
|
|
1257
|
-
JsonDeserializedRecursion<T[K], Controls,
|
|
1358
|
+
JsonDeserializedRecursion<T[K], Controls, TAncestorTypes>
|
|
1258
1359
|
>;
|
|
1259
1360
|
}
|
|
1260
1361
|
: /* not an array => test for exactly `object` */ IsExactlyObject<T> extends true
|
|
1261
1362
|
? /* `object` => */ Controls["DegenerateNonNullObjectSubstitute"]
|
|
1262
1363
|
: /* test for enum like types */ IfEnumLike<T> extends never
|
|
1263
1364
|
? /* enum or similar simple type (return as-is) => */ T
|
|
1264
|
-
: /* test for matching
|
|
1265
|
-
? /*
|
|
1365
|
+
: /* test for matching OpaqueJson* types */ T extends AnyOpaqueJsonType
|
|
1366
|
+
? /* OpaqueJson* type => */ JsonDeserializedOpaqueConversion<T, Controls>
|
|
1266
1367
|
: /* property bag => */ FlattenIntersection<
|
|
1267
1368
|
/* properties with symbol keys or wholly unsupported values are removed */
|
|
1268
1369
|
{
|
|
1269
1370
|
/* properties with defined values are recursed */
|
|
1270
1371
|
[K in keyof T as NonSymbolWithDeserializablePropertyOf<
|
|
1271
1372
|
T,
|
|
1272
|
-
|
|
1373
|
+
[
|
|
1374
|
+
...Controls["AllowExactly"],
|
|
1375
|
+
Controls["RecursionMarkerAllowed"],
|
|
1376
|
+
],
|
|
1273
1377
|
Controls["AllowExtensionOf"],
|
|
1274
1378
|
K
|
|
1275
|
-
>]: JsonDeserializedRecursion<
|
|
1276
|
-
T[K],
|
|
1277
|
-
Controls,
|
|
1278
|
-
RecurseLimit,
|
|
1279
|
-
TAncestorTypes
|
|
1280
|
-
>;
|
|
1379
|
+
>]: JsonDeserializedRecursion<T[K], Controls, TAncestorTypes>;
|
|
1281
1380
|
} & {
|
|
1282
|
-
/* properties that may have undefined values are optional */
|
|
1283
|
-
[K in keyof T as
|
|
1381
|
+
/* literal properties that may have undefined values are optional */
|
|
1382
|
+
[K in keyof T as NonSymbolLiteralWithPossiblyDeserializablePropertyOf<
|
|
1284
1383
|
T,
|
|
1285
|
-
|
|
1384
|
+
[
|
|
1385
|
+
...Controls["AllowExactly"],
|
|
1386
|
+
Controls["RecursionMarkerAllowed"],
|
|
1387
|
+
],
|
|
1286
1388
|
Controls["AllowExtensionOf"],
|
|
1287
1389
|
K
|
|
1288
|
-
>]?: JsonDeserializedRecursion<
|
|
1289
|
-
T[K],
|
|
1290
|
-
Controls,
|
|
1291
|
-
RecurseLimit,
|
|
1292
|
-
TAncestorTypes
|
|
1293
|
-
>;
|
|
1390
|
+
>]?: JsonDeserializedRecursion<T[K], Controls, TAncestorTypes>;
|
|
1294
1391
|
}
|
|
1295
1392
|
>
|
|
1296
1393
|
: /* not an object => */ never;
|
|
@@ -1299,6 +1396,13 @@ export namespace InternalUtilityTypes {
|
|
|
1299
1396
|
|
|
1300
1397
|
// #region *Readonly implementations
|
|
1301
1398
|
|
|
1399
|
+
/**
|
|
1400
|
+
* Recursion limit is the count of `+` that prefix it when string.
|
|
1401
|
+
*
|
|
1402
|
+
* @system
|
|
1403
|
+
*/
|
|
1404
|
+
export type RecursionLimit = `+${string}` | 0;
|
|
1405
|
+
|
|
1302
1406
|
/**
|
|
1303
1407
|
* If `T` is a `Map<K,V>` or `ReadonlyMap<K,V>`, returns `ReadonlyMap` and,
|
|
1304
1408
|
* if `T` extends `DeepenedGenerics`, {@link DeepReadonly} is applied to
|