@fluidframework/core-interfaces 2.41.0 → 2.42.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/exposedInternalUtilityTypes.d.ts +56 -4
  3. package/dist/exposedInternalUtilityTypes.d.ts.map +1 -1
  4. package/dist/exposedInternalUtilityTypes.js.map +1 -1
  5. package/dist/exposedUtilityTypes.d.ts +1 -0
  6. package/dist/exposedUtilityTypes.d.ts.map +1 -1
  7. package/dist/exposedUtilityTypes.js.map +1 -1
  8. package/dist/internal.d.ts +10 -0
  9. package/dist/internal.d.ts.map +1 -1
  10. package/dist/internal.js.map +1 -1
  11. package/dist/jsonSerializable.d.ts +2 -2
  12. package/dist/jsonSerializable.js.map +1 -1
  13. package/dist/jsonUtils.d.ts +70 -0
  14. package/dist/jsonUtils.d.ts.map +1 -0
  15. package/dist/jsonUtils.js +7 -0
  16. package/dist/jsonUtils.js.map +1 -0
  17. package/dist/opaqueJson.d.ts +60 -0
  18. package/dist/opaqueJson.d.ts.map +1 -0
  19. package/dist/opaqueJson.js +8 -0
  20. package/dist/opaqueJson.js.map +1 -0
  21. package/lib/exposedInternalUtilityTypes.d.ts +56 -4
  22. package/lib/exposedInternalUtilityTypes.d.ts.map +1 -1
  23. package/lib/exposedInternalUtilityTypes.js.map +1 -1
  24. package/lib/exposedUtilityTypes.d.ts +1 -0
  25. package/lib/exposedUtilityTypes.d.ts.map +1 -1
  26. package/lib/exposedUtilityTypes.js.map +1 -1
  27. package/lib/internal.d.ts +10 -0
  28. package/lib/internal.d.ts.map +1 -1
  29. package/lib/internal.js.map +1 -1
  30. package/lib/jsonSerializable.d.ts +2 -2
  31. package/lib/jsonSerializable.js.map +1 -1
  32. package/lib/jsonUtils.d.ts +70 -0
  33. package/lib/jsonUtils.d.ts.map +1 -0
  34. package/lib/jsonUtils.js +6 -0
  35. package/lib/jsonUtils.js.map +1 -0
  36. package/lib/opaqueJson.d.ts +60 -0
  37. package/lib/opaqueJson.d.ts.map +1 -0
  38. package/lib/opaqueJson.js +6 -0
  39. package/lib/opaqueJson.js.map +1 -0
  40. package/package.json +2 -2
  41. package/src/exposedInternalUtilityTypes.ts +186 -80
  42. package/src/exposedUtilityTypes.ts +1 -0
  43. package/src/internal.ts +24 -0
  44. package/src/jsonSerializable.ts +2 -2
  45. package/src/jsonUtils.ts +90 -0
  46. package/src/opaqueJson.ts +87 -0
@@ -0,0 +1,60 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { BrandedType } from "./brandedType.js";
6
+ /**
7
+ * Placeholder for value that is known to be JSON because it has been
8
+ * deserialized (`T` filtered through {@link JsonDeserialized} as out value).
9
+ *
10
+ * @remarks
11
+ * Usage:
12
+ *
13
+ * - Cast to with `as unknown as OpaqueJsonDeserialized<T>` when value `T`
14
+ * has been filtered through {@link JsonDeserialized}.
15
+ *
16
+ * - Cast from with `as unknown as JsonDeserialized<T>` when "instance" will
17
+ * be read.
18
+ *
19
+ * @sealed
20
+ * @beta
21
+ */
22
+ export declare class OpaqueJsonDeserialized<T, in out Option_AllowExactly extends unknown[] = [], out Option_AllowExtensionOf = never> extends BrandedType<"JsonDeserialized"> {
23
+ protected readonly JsonDeserialized: {
24
+ Type: T;
25
+ Options: {
26
+ AllowExactly: Option_AllowExactly;
27
+ AllowExtensionOf: Option_AllowExtensionOf;
28
+ };
29
+ };
30
+ protected readonly Option_AllowExactly_Invariance: (Option_AllowExactly: Option_AllowExactly) => void;
31
+ private constructor();
32
+ }
33
+ /**
34
+ * Placeholder for value that is known to be JSON because it will have been
35
+ * serialized checked (`T` filtered through {@link JsonSerializable} before "created").
36
+ *
37
+ * @remarks
38
+ * Usage:
39
+ *
40
+ * - Cast to with `as unknown as OpaqueJsonSerializable<T>` when value `T`
41
+ * has been filtered through {@link JsonSerializable}.
42
+ *
43
+ * - Cast from with `as unknown as JsonSerializable<T>` or `as unknown as T`
44
+ * when "instance" will be forwarded along.
45
+ *
46
+ * @sealed
47
+ * @beta
48
+ */
49
+ export declare class OpaqueJsonSerializable<T, in out Option_AllowExactly extends unknown[] = [], out Option_AllowExtensionOf = never> extends BrandedType<"JsonSerializable"> {
50
+ protected readonly JsonSerializable: {
51
+ Type: T;
52
+ Options: {
53
+ AllowExactly: Option_AllowExactly;
54
+ AllowExtensionOf: Option_AllowExtensionOf;
55
+ };
56
+ };
57
+ protected readonly Option_AllowExactly_Invariance: (Option_AllowExactly: Option_AllowExactly) => void;
58
+ private constructor();
59
+ }
60
+ //# sourceMappingURL=opaqueJson.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opaqueJson.d.ts","sourceRoot":"","sources":["../src/opaqueJson.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,OAAO,OAAO,sBAAsB,CAC1C,CAAC,EAMD,EAAE,CAAC,GAAG,CAAC,mBAAmB,SAAS,OAAO,EAAE,GAAG,EAAE,EACjD,GAAG,CAAC,uBAAuB,GAAG,KAAK,CAClC,SAAQ,WAAW,CAAC,kBAAkB,CAAC;IACxC,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE;QACpC,IAAI,EAAE,CAAC,CAAC;QACR,OAAO,EAAE;YACR,YAAY,EAAE,mBAAmB,CAAC;YAClC,gBAAgB,EAAE,uBAAuB,CAAC;SAC1C,CAAC;KACF,CAAC;IAEF,SAAS,CAAC,QAAQ,CAAC,8BAA8B,EAAE,CAClD,mBAAmB,EAAE,mBAAmB,KACpC,IAAI,CAAC;IACV,OAAO;CACP;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,OAAO,OAAO,sBAAsB,CAC1C,CAAC,EAMD,EAAE,CAAC,GAAG,CAAC,mBAAmB,SAAS,OAAO,EAAE,GAAG,EAAE,EACjD,GAAG,CAAC,uBAAuB,GAAG,KAAK,CAElC,SAAQ,WAAW,CAAC,kBAAkB,CAAC;IACxC,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE;QACpC,IAAI,EAAE,CAAC,CAAC;QACR,OAAO,EAAE;YACR,YAAY,EAAE,mBAAmB,CAAC;YAClC,gBAAgB,EAAE,uBAAuB,CAAC;SAC1C,CAAC;KACF,CAAC;IAEF,SAAS,CAAC,QAAQ,CAAC,8BAA8B,EAAE,CAClD,mBAAmB,EAAE,mBAAmB,KACpC,IAAI,CAAC;IACV,OAAO;CACP"}
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { BrandedType } from "./brandedType.js";
6
+ //# sourceMappingURL=opaqueJson.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opaqueJson.js","sourceRoot":"","sources":["../src/opaqueJson.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { BrandedType } from \"./brandedType.js\";\n\n/**\n * Placeholder for value that is known to be JSON because it has been\n * deserialized (`T` filtered through {@link JsonDeserialized} as out value).\n *\n * @remarks\n * Usage:\n *\n * - Cast to with `as unknown as OpaqueJsonDeserialized<T>` when value `T`\n * has been filtered through {@link JsonDeserialized}.\n *\n * - Cast from with `as unknown as JsonDeserialized<T>` when \"instance\" will\n * be read.\n *\n * @sealed\n * @beta\n */\nexport declare class OpaqueJsonDeserialized<\n\tT,\n\t// These options are split from typical `JsonDeserializedOptions` as this type\n\t// requires correct variance per the two options and AllowExactly has special\n\t// variance and must be treated as invariant. In actuality, each member of the\n\t// AllowExactly tuple is invariant, but tuple as a set is covariant. This is not\n\t// expressible in TypeScript.\n\tin out Option_AllowExactly extends unknown[] = [],\n\tout Option_AllowExtensionOf = never,\n> extends BrandedType<\"JsonDeserialized\"> {\n\tprotected readonly JsonDeserialized: {\n\t\tType: T;\n\t\tOptions: {\n\t\t\tAllowExactly: Option_AllowExactly;\n\t\t\tAllowExtensionOf: Option_AllowExtensionOf;\n\t\t};\n\t};\n\t// Option_AllowExactly is covariant from above. This removes covariance, leaving only invariance.\n\tprotected readonly Option_AllowExactly_Invariance: (\n\t\tOption_AllowExactly: Option_AllowExactly,\n\t) => void;\n\tprivate constructor();\n}\n\n/**\n * Placeholder for value that is known to be JSON because it will have been\n * serialized checked (`T` filtered through {@link JsonSerializable} before \"created\").\n *\n * @remarks\n * Usage:\n *\n * - Cast to with `as unknown as OpaqueJsonSerializable<T>` when value `T`\n * has been filtered through {@link JsonSerializable}.\n *\n * - Cast from with `as unknown as JsonSerializable<T>` or `as unknown as T`\n * when \"instance\" will be forwarded along.\n *\n * @sealed\n * @beta\n */\nexport declare class OpaqueJsonSerializable<\n\tT,\n\t// These options are split from typical `JsonSerializableOptions` as this type\n\t// requires correct variance per the two options and AllowExactly has special\n\t// variance and must be treated as invariant. In actuality, each member of the\n\t// AllowExactly tuple is invariant, but tuple as a set is covariant. This is not\n\t// expressible in TypeScript.\n\tin out Option_AllowExactly extends unknown[] = [],\n\tout Option_AllowExtensionOf = never,\n\t// JsonSerializableOptions.IgnoreInaccessibleMembers is ignored\n> extends BrandedType<\"JsonSerializable\"> {\n\tprotected readonly JsonSerializable: {\n\t\tType: T;\n\t\tOptions: {\n\t\t\tAllowExactly: Option_AllowExactly;\n\t\t\tAllowExtensionOf: Option_AllowExtensionOf;\n\t\t};\n\t};\n\t// Option_AllowExactly is covariant from above. This removes covariance, leaving only invariance.\n\tprotected readonly Option_AllowExactly_Invariance: (\n\t\tOption_AllowExactly: Option_AllowExactly,\n\t) => void;\n\tprivate constructor();\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/core-interfaces",
3
- "version": "2.41.0",
3
+ "version": "2.42.0",
4
4
  "description": "Fluid object interfaces",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -76,7 +76,7 @@
76
76
  "@fluid-tools/build-cli": "^0.55.0",
77
77
  "@fluidframework/build-common": "^2.0.3",
78
78
  "@fluidframework/build-tools": "^0.55.0",
79
- "@fluidframework/core-interfaces-previous": "npm:@fluidframework/core-interfaces@2.40.0",
79
+ "@fluidframework/core-interfaces-previous": "npm:@fluidframework/core-interfaces@2.41.0",
80
80
  "@fluidframework/eslint-config-fluid": "^5.7.4",
81
81
  "@microsoft/api-extractor": "7.52.8",
82
82
  "@types/mocha": "^10.0.10",
@@ -12,6 +12,7 @@ import type {
12
12
  SerializationErrorPerUndefinedArrayElement,
13
13
  } from "./jsonSerializationErrors.js";
14
14
  import type { JsonTypeWith, NonNullJsonObjectWith, ReadonlyJsonTypeWith } from "./jsonType.js";
15
+ import type { OpaqueJsonDeserialized, OpaqueJsonSerializable } from "./opaqueJson.js";
15
16
 
16
17
  /**
17
18
  * Unique symbol for recursion meta-typing.
@@ -557,6 +558,20 @@ export namespace InternalUtilityTypes {
557
558
  }
558
559
  : T;
559
560
 
561
+ /**
562
+ * Convenience constraint for any Opaque Json type.
563
+ *
564
+ * @remarks
565
+ * Use in extends check: `T extends AnyOpaqueJsonType`
566
+ *
567
+ * @system
568
+ */
569
+ export type AnyOpaqueJsonType =
570
+ /* eslint-disable @typescript-eslint/no-explicit-any -- must use `any` for invariant constraint override */
571
+ | OpaqueJsonSerializable<unknown, any, unknown>
572
+ | OpaqueJsonDeserialized<unknown, any, unknown>;
573
+ /* eslint-enable @typescript-eslint/no-explicit-any */
574
+
560
575
  /**
561
576
  * Extracts Function portion from an intersection (&) type returning
562
577
  * the extracted portion in the `function` property or `unknown` if
@@ -775,15 +790,23 @@ export namespace InternalUtilityTypes {
775
790
  AllowExtensionOf: Options extends { AllowExtensionOf: unknown }
776
791
  ? Options["AllowExtensionOf"]
777
792
  : never;
778
- // There Substitute type could be extracted to helper type, but are kept explicit here
779
- // to make JsonTypeWith show explicitly in results for users, rather
780
- // than either the helper type name or a partially unrolled version.
781
- DegenerateSubstitute: JsonTypeWith<
782
- | (Options extends { AllowExactly: unknown[] }
783
- ? TupleToUnion<Options["AllowExactly"]>
784
- : never)
785
- | (Options extends { AllowExtensionOf: unknown } ? Options["AllowExtensionOf"] : never)
786
- >;
793
+ // The Substitute type could be extracted to helper type, but is kept explicit here
794
+ // to make JsonTypeWith and OpaqueJsonSerializable show explicitly in results for
795
+ // users, rather than either the helper type name or a partially unrolled version.
796
+ DegenerateSubstitute:
797
+ | JsonTypeWith<
798
+ | (Options extends { AllowExactly: unknown[] }
799
+ ? TupleToUnion<Options["AllowExactly"]>
800
+ : never)
801
+ | (Options extends { AllowExtensionOf: unknown }
802
+ ? Options["AllowExtensionOf"]
803
+ : never)
804
+ >
805
+ | OpaqueJsonSerializable<
806
+ unknown,
807
+ Options extends { AllowExactly: unknown[] } ? Options["AllowExactly"] : [],
808
+ Options extends { AllowExtensionOf: unknown } ? Options["AllowExtensionOf"] : never
809
+ >;
787
810
  } extends infer Controls
788
811
  ? /* Controls should always satisfy FilterControlsWithSubstitution, but Typescript wants a check */
789
812
  Controls extends FilterControlsWithSubstitution
@@ -796,8 +819,14 @@ export namespace InternalUtilityTypes {
796
819
  T,
797
820
  {
798
821
  AllowExactly: Controls["AllowExactly"];
799
- // Add in primitives that may be branded to ignore intersection classes
800
- AllowExtensionOf: Controls["AllowExtensionOf"] | boolean | number | string;
822
+ AllowExtensionOf:
823
+ | Controls["AllowExtensionOf"]
824
+ // Add in primitives that may be branded to ignore intersection classes
825
+ | boolean
826
+ | number
827
+ | string
828
+ // Add in Opaque Json types
829
+ | AnyOpaqueJsonType;
801
830
  DegenerateSubstitute: Controls["DegenerateSubstitute"];
802
831
  },
803
832
  "found non-publics",
@@ -825,6 +854,47 @@ export namespace InternalUtilityTypes {
825
854
  : never /* FilterControlsWithSubstitution assert else; should never be reached */
826
855
  : never /* unreachable else for infer */;
827
856
 
857
+ /**
858
+ * Handle Opaque Json types for {@link JsonSerializable}.
859
+ *
860
+ * @remarks
861
+ * {@link OpaqueJsonSerializable} and {@link OpaqueJsonDeserialized} instances
862
+ * are limited to `Controls` given context supports.
863
+ * `T` from the original Opaque type is preserved. In the case that this now
864
+ * produces an improper type such as a `bigint` being let through that is no
865
+ * longer supported, then the variance of `Controls` is expected to raise
866
+ * the incompatibility error.
867
+ *
868
+ * @privateRemarks
869
+ * Additional intersections beyond {@link OpaqueJsonSerializable},
870
+ * {@link OpaqueJsonDeserialized}, or intersected matching opaque pair are
871
+ * not correctly filtered as need is not expected.
872
+ *
873
+ * @system
874
+ */
875
+ export type JsonSerializableOpaqueAllowances<
876
+ T extends AnyOpaqueJsonType,
877
+ Controls extends FilterControlsWithSubstitution,
878
+ > = /* eslint-disable @typescript-eslint/no-explicit-any -- must use `any` for invariant constraint override */
879
+ /* infer underlying data type */ T extends
880
+ | OpaqueJsonSerializable<infer TData, any, unknown>
881
+ | OpaqueJsonDeserialized<infer TData, any, unknown>
882
+ ? T extends OpaqueJsonSerializable<TData, any, unknown> &
883
+ OpaqueJsonDeserialized<TData, any, unknown>
884
+ ? OpaqueJsonSerializable<TData, Controls["AllowExactly"], Controls["AllowExtensionOf"]> &
885
+ OpaqueJsonDeserialized<TData, Controls["AllowExactly"], Controls["AllowExtensionOf"]>
886
+ : T extends OpaqueJsonSerializable<TData, any, unknown>
887
+ ? OpaqueJsonSerializable<TData, Controls["AllowExactly"], Controls["AllowExtensionOf"]>
888
+ : T extends OpaqueJsonDeserialized<TData, any, unknown>
889
+ ? OpaqueJsonDeserialized<
890
+ TData,
891
+ Controls["AllowExactly"],
892
+ Controls["AllowExtensionOf"]
893
+ >
894
+ : "internal error: failed to determine Opaque Json type"
895
+ : never;
896
+ /* eslint-enable @typescript-eslint/no-explicit-any */
897
+
828
898
  /**
829
899
  * Essentially a check for a template literal that has $\{string\} or
830
900
  * $\{number\} in the pattern. Just `string` and/or `number` also match.
@@ -933,45 +1003,50 @@ export namespace InternalUtilityTypes {
933
1003
  >
934
1004
  : /* test for enum like types */ IfEnumLike<T> extends never
935
1005
  ? /* enum or similar simple type (return as-is) => */ T
936
- : /* property bag => */ FlattenIntersection<
937
- {
938
- /* required properties are recursed and may not have undefined values. */
939
- [K in keyof T as RequiredNonSymbolKeysOf<
940
- T,
941
- K
942
- >]-?: IfPossiblyUndefinedProperty<
943
- K,
944
- T[K],
945
- {
946
- IfPossiblyUndefined: {
947
- ["error required property may not allow `undefined` value"]: never;
948
- };
949
- IfUnknownNonIndexed: {
950
- ["error required property may not allow `unknown` value"]: never;
951
- };
952
- Otherwise: JsonSerializableFilter<
953
- T[K],
954
- Controls,
955
- [TNextAncestor, ...TAncestorTypes]
956
- >;
957
- }
958
- >;
959
- } & {
960
- /* optional properties are recursed and, when exactOptionalPropertyTypes is
1006
+ : /* test for Opaque Json types */ T extends AnyOpaqueJsonType
1007
+ ? /* Opaque Json type => */ JsonSerializableOpaqueAllowances<
1008
+ T,
1009
+ Controls
1010
+ >
1011
+ : /* property bag => */ FlattenIntersection<
1012
+ {
1013
+ /* required properties are recursed and may not have undefined values. */
1014
+ [K in keyof T as RequiredNonSymbolKeysOf<
1015
+ T,
1016
+ K
1017
+ >]-?: IfPossiblyUndefinedProperty<
1018
+ K,
1019
+ T[K],
1020
+ {
1021
+ IfPossiblyUndefined: {
1022
+ ["error required property may not allow `undefined` value"]: never;
1023
+ };
1024
+ IfUnknownNonIndexed: {
1025
+ ["error required property may not allow `unknown` value"]: never;
1026
+ };
1027
+ Otherwise: JsonSerializableFilter<
1028
+ T[K],
1029
+ Controls,
1030
+ [TNextAncestor, ...TAncestorTypes]
1031
+ >;
1032
+ }
1033
+ >;
1034
+ } & {
1035
+ /* optional properties are recursed and, when exactOptionalPropertyTypes is
961
1036
  false, are allowed to preserve undefined value type. */
962
- [K in keyof T as OptionalNonSymbolKeysOf<
963
- T,
964
- K
965
- >]?: JsonSerializableFilter<
966
- T[K],
967
- Controls,
968
- [TNextAncestor, ...TAncestorTypes]
969
- >;
970
- } & {
971
- /* symbol properties are rejected */
972
- [K in keyof T & symbol]: never;
973
- }
974
- >
1037
+ [K in keyof T as OptionalNonSymbolKeysOf<
1038
+ T,
1039
+ K
1040
+ >]?: JsonSerializableFilter<
1041
+ T[K],
1042
+ Controls,
1043
+ [TNextAncestor, ...TAncestorTypes]
1044
+ >;
1045
+ } & {
1046
+ /* symbol properties are rejected */
1047
+ [K in keyof T & symbol]: never;
1048
+ }
1049
+ >
975
1050
  : /* not an object => */ never
976
1051
  : /* function => */ never;
977
1052
 
@@ -1015,7 +1090,7 @@ export namespace InternalUtilityTypes {
1015
1090
  AllowExtensionOf: Options extends { AllowExtensionOf: unknown }
1016
1091
  ? Options["AllowExtensionOf"]
1017
1092
  : never;
1018
- // There Substitute types could be extracted to helper type, but are kept explicit here
1093
+ // The Substitute types could be extracted to helper type, but are kept explicit here
1019
1094
  // to make JsonTypeWith/NonNullJsonObjectWith show explicitly in results for users, rather
1020
1095
  // than either the helper type name or a partially unrolled version.
1021
1096
  DegenerateSubstitute: JsonTypeWith<
@@ -1109,6 +1184,35 @@ export namespace InternalUtilityTypes {
1109
1184
  : Controls["DegenerateSubstitute"]
1110
1185
  : JsonDeserializedFilter<T, Controls, RecurseLimit, TAncestorTypes | T>;
1111
1186
 
1187
+ /**
1188
+ * Handle Opaque Json types for {@link JsonDeserialized}.
1189
+ *
1190
+ * @remarks
1191
+ * {@link OpaqueJsonSerializable} instances are converted to {@link OpaqueJsonDeserialized}.
1192
+ * The `AllowExactly` and `AllowExtensionOf` properties are set to match the given `Controls`.
1193
+ * The data type is kept exactly as-is to avoid processing in generic contexts that can't
1194
+ * produce a meaningful result. The data type should always be filtered through
1195
+ * {@link JsonDeserialized} when {@link OpaqueJsonDeserialized} is cracked open. So, really
1196
+ * the filtering is just deferred.
1197
+ *
1198
+ * @privateRemarks
1199
+ * Additional intersections beyond {@link OpaqueJsonSerializable},
1200
+ * {@link OpaqueJsonDeserialized}, or intersected matching opaque pair are
1201
+ * not correctly filtered as need is not expected.
1202
+ *
1203
+ * @system
1204
+ */
1205
+ export type JsonDeserializedOpaqueConversion<
1206
+ T extends AnyOpaqueJsonType,
1207
+ Controls extends FilterControls,
1208
+ > = /* eslint-disable @typescript-eslint/no-explicit-any -- must use `any` for invariant constraint override */
1209
+ T extends
1210
+ | OpaqueJsonSerializable<infer TData, any, unknown>
1211
+ | OpaqueJsonDeserialized<infer TData, any, unknown>
1212
+ ? OpaqueJsonDeserialized<TData, Controls["AllowExactly"], Controls["AllowExtensionOf"]>
1213
+ : "internal error: failed to determine Opaque Json type";
1214
+ /* eslint-enable @typescript-eslint/no-explicit-any */
1215
+
1112
1216
  /**
1113
1217
  * Core implementation of {@link JsonDeserialized}.
1114
1218
  *
@@ -1157,36 +1261,38 @@ export namespace InternalUtilityTypes {
1157
1261
  ? /* `object` => */ Controls["DegenerateNonNullObjectSubstitute"]
1158
1262
  : /* test for enum like types */ IfEnumLike<T> extends never
1159
1263
  ? /* enum or similar simple type (return as-is) => */ T
1160
- : /* property bag => */ FlattenIntersection<
1161
- /* properties with symbol keys or wholly unsupported values are removed */
1162
- {
1163
- /* properties with defined values are recursed */
1164
- [K in keyof T as NonSymbolWithDeserializablePropertyOf<
1165
- T,
1166
- Controls["AllowExactly"],
1167
- Controls["AllowExtensionOf"],
1168
- K
1169
- >]: JsonDeserializedRecursion<
1170
- T[K],
1171
- Controls,
1172
- RecurseLimit,
1173
- TAncestorTypes
1174
- >;
1175
- } & {
1176
- /* properties that may have undefined values are optional */
1177
- [K in keyof T as NonSymbolWithPossiblyDeserializablePropertyOf<
1178
- T,
1179
- Controls["AllowExactly"],
1180
- Controls["AllowExtensionOf"],
1181
- K
1182
- >]?: JsonDeserializedRecursion<
1183
- T[K],
1184
- Controls,
1185
- RecurseLimit,
1186
- TAncestorTypes
1187
- >;
1188
- }
1189
- >
1264
+ : /* test for matching Opaque Json types */ T extends AnyOpaqueJsonType
1265
+ ? /* Opaque Json type => */ JsonDeserializedOpaqueConversion<T, Controls>
1266
+ : /* property bag => */ FlattenIntersection<
1267
+ /* properties with symbol keys or wholly unsupported values are removed */
1268
+ {
1269
+ /* properties with defined values are recursed */
1270
+ [K in keyof T as NonSymbolWithDeserializablePropertyOf<
1271
+ T,
1272
+ Controls["AllowExactly"],
1273
+ Controls["AllowExtensionOf"],
1274
+ K
1275
+ >]: JsonDeserializedRecursion<
1276
+ T[K],
1277
+ Controls,
1278
+ RecurseLimit,
1279
+ TAncestorTypes
1280
+ >;
1281
+ } & {
1282
+ /* properties that may have undefined values are optional */
1283
+ [K in keyof T as NonSymbolWithPossiblyDeserializablePropertyOf<
1284
+ T,
1285
+ Controls["AllowExactly"],
1286
+ Controls["AllowExtensionOf"],
1287
+ K
1288
+ >]?: JsonDeserializedRecursion<
1289
+ T[K],
1290
+ Controls,
1291
+ RecurseLimit,
1292
+ TAncestorTypes
1293
+ >;
1294
+ }
1295
+ >
1190
1296
  : /* not an object => */ never;
1191
1297
 
1192
1298
  // #endregion
@@ -21,6 +21,7 @@ export type {
21
21
  NonNullJsonObjectWith,
22
22
  ReadonlyJsonTypeWith,
23
23
  } from "./jsonType.js";
24
+ export type { OpaqueJsonDeserialized, OpaqueJsonSerializable } from "./opaqueJson.js";
24
25
  export type { ShallowReadonly } from "./shallowReadonly.js";
25
26
 
26
27
  export type {
package/src/internal.ts CHANGED
@@ -10,6 +10,8 @@ export * from "./index.js";
10
10
  // index.js is listed as the runtime file. This is done so that all imports are
11
11
  // using the same outer runtime file. (Could be changed if needed.)
12
12
 
13
+ export type { JsonTypeToOpaqueJson, OpaqueJsonToJsonType } from "./jsonUtils.js";
14
+
13
15
  // Export set of utility types re-tagged as internal for FF client convenience.
14
16
  // These types are not intended for direct use by customers and api-extractor will
15
17
  // flag misuse. If an externally visible version of these types is needed, import
@@ -28,6 +30,10 @@ import type {
28
30
  JsonTypeWith as ExposedJsonTypeWith,
29
31
  ReadonlyNonNullJsonObjectWith as ExposedReadonlyNonNullJsonObjectWith,
30
32
  } from "./jsonType.js";
33
+ import type {
34
+ OpaqueJsonDeserialized as ExposedOpaqueJsonDeserialized,
35
+ OpaqueJsonSerializable as ExposedOpaqueJsonSerializable,
36
+ } from "./opaqueJson.js";
31
37
 
32
38
  // Note: There are no docs for these re-exports. `@inheritdoc` cannot be used as:
33
39
  // 1. api-extractor does not support renames.
@@ -71,6 +77,24 @@ export type JsonTypeWith<T> = ExposedJsonTypeWith<T>;
71
77
  */
72
78
  export type ReadonlyNonNullJsonObjectWith<T> = ExposedReadonlyNonNullJsonObjectWith<T>;
73
79
 
80
+ /**
81
+ * @internal
82
+ */
83
+ export type OpaqueJsonDeserialized<
84
+ T,
85
+ Option_AllowExactly extends unknown[] = [],
86
+ Option_AllowExtensionOf = never,
87
+ > = ExposedOpaqueJsonDeserialized<T, Option_AllowExactly, Option_AllowExtensionOf>;
88
+
89
+ /**
90
+ * @internal
91
+ */
92
+ export type OpaqueJsonSerializable<
93
+ T,
94
+ Option_AllowExactly extends unknown[] = [],
95
+ Option_AllowExtensionOf = never,
96
+ > = ExposedOpaqueJsonSerializable<T, Option_AllowExactly, Option_AllowExtensionOf>;
97
+
74
98
  /**
75
99
  * @internal
76
100
  */
@@ -89,8 +89,8 @@ export interface JsonSerializableOptions {
89
89
  * Also, `JsonSerializable<T>` does not prevent the construction of circular references.
90
90
  *
91
91
  * Specifying `JsonSerializable<unknown>` or `JsonSerializable<any>` yields a type
92
- * alias for {@link JsonTypeWith}`<never>` and should not be used if precise type
93
- * safety is desired.
92
+ * alias for {@link JsonTypeWith}`<never>` | {@link OpaqueJsonSerializable}`<unknown>`
93
+ * and should not be used if precise type safety is desired.
94
94
  *
95
95
  * Class instances are indistinguishable from general objects by type checking
96
96
  * unless they have non-public members.
@@ -0,0 +1,90 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import type { JsonDeserialized } from "./jsonDeserialized.js";
7
+ import type { JsonSerializable, JsonSerializableOptions } from "./jsonSerializable.js";
8
+ import type { OpaqueJsonDeserialized, OpaqueJsonSerializable } from "./opaqueJson.js";
9
+
10
+ /**
11
+ * Helper to return an Opaque Json type version of Json type
12
+ *
13
+ * @remarks
14
+ * To use this helper, create a helper function that filters type `T` through at
15
+ * least {@link JsonSerializable} and optionally {@link JsonDeserialized}. Then
16
+ * cast value through `unknown as JsonTypeToOpaqueJson<T, Options>`, where
17
+ * `Options` reflects the serialization capabilities of that area.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * function castToOpaqueJson<T>(value: JsonSerializable<T>): JsonTypeToOpaqueJson<T> {
22
+ * return value as unknown as JsonTypeToOpaqueJson<T>;
23
+ * }
24
+ * ```
25
+ *
26
+ * @internal
27
+ */
28
+ export type JsonTypeToOpaqueJson<
29
+ T,
30
+ Options extends JsonSerializableOptions = {
31
+ AllowExactly: [];
32
+ AllowExtensionOf: never;
33
+ },
34
+ > = T extends JsonSerializable<T, Options> & JsonDeserialized<T, Options>
35
+ ? OpaqueJsonSerializable<
36
+ T,
37
+ Options extends { AllowExactly: unknown[] } ? Options["AllowExactly"] : [],
38
+ Options extends { AllowExtensionOf: unknown } ? Options["AllowExtensionOf"] : never
39
+ > &
40
+ OpaqueJsonDeserialized<
41
+ T,
42
+ Options extends { AllowExactly: unknown[] } ? Options["AllowExactly"] : [],
43
+ Options extends { AllowExtensionOf: unknown } ? Options["AllowExtensionOf"] : never
44
+ >
45
+ : T extends JsonDeserialized<T, Options>
46
+ ? OpaqueJsonDeserialized<
47
+ T,
48
+ Options extends { AllowExactly: unknown[] } ? Options["AllowExactly"] : [],
49
+ Options extends { AllowExtensionOf: unknown } ? Options["AllowExtensionOf"] : never
50
+ >
51
+ : T extends JsonSerializable<T, Options>
52
+ ? OpaqueJsonSerializable<
53
+ T,
54
+ Options extends { AllowExactly: unknown[] } ? Options["AllowExactly"] : [],
55
+ Options extends { AllowExtensionOf: unknown } ? Options["AllowExtensionOf"] : never
56
+ >
57
+ : never;
58
+
59
+ /**
60
+ * Helper to extract Json type from an Opaque Json type
61
+ *
62
+ * @remarks
63
+ * This type only works with basic serialization capabilities (options).
64
+ * Attempts to make `Options` generic resulted in infinite recursion
65
+ * in TypeScript compiler that was not understood, so this type only
66
+ * supports TJson (value type) variance.
67
+ *
68
+ * To use this helper, create a helper function that accepts
69
+ * `OpaqueJsonSerializable<unknown> | OpaqueJsonDeserialized<unknown>`.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * function exposeFromOpaqueJson<TOpaque extends OpaqueJsonSerializable<unknown> | OpaqueJsonDeserialized<unknown>>(
74
+ * opaque: TOpaque,
75
+ * ): OpaqueJsonToJsonType<TOpaque> {
76
+ * return opaque as unknown as OpaqueJsonToJsonType<TOpaque>;
77
+ * }
78
+ * ```
79
+ *
80
+ * @internal
81
+ */
82
+ export type OpaqueJsonToJsonType<
83
+ TOpaque extends OpaqueJsonSerializable<unknown> | OpaqueJsonDeserialized<unknown>,
84
+ > = TOpaque extends OpaqueJsonSerializable<infer TJson> & OpaqueJsonDeserialized<infer TJson>
85
+ ? JsonSerializable<TJson> & JsonDeserialized<TJson>
86
+ : TOpaque extends OpaqueJsonDeserialized<infer TJson>
87
+ ? JsonDeserialized<TJson>
88
+ : TOpaque extends OpaqueJsonSerializable<infer TJson>
89
+ ? JsonSerializable<TJson>
90
+ : never;