Skip to content

Commit 4da8ea2

Browse files
committed
feat(client-core-interfaces): JsonString
with `JsonStringify` and `JsonParse` utilities Test cases added: ```text JsonString ✔ `JsonString<string>` is assignable to `string` ✔ `string` is not assignable to `JsonString<unknown>` ✔ object is not assignable to `JsonString<unknown>` is covariant over T ✔ `JsonString<"literal">` is assignable to `JsonString<string>` ✔ `JsonString<Record<string, number>>` is assignable to `JsonString<Record<string, number | undefined>>` ✔ `JsonString<Record<string, number | undefined>>` is assignable to `JsonString<Record<string, unknown>>` ✔ `JsonString<bigint>` is assignable to `JsonString<unknown>` is not contravariant over T ✔ `JsonString<string>` is not assignable to `JsonString<"literal">` ✔ `JsonString<Record<string, number | undefined>>` is not assignable to `JsonString<Record<string, number>>` ✔ `JsonString<unknown>` is not assignable to `JsonString<bigint>` JsonStringify and JsonParse expected usage supports primitive types ✔ `boolean` ... ✔ branded `string` ✔ `JsonString<string>` ✔ `JsonString<{ arrayOfNumbers: number[] }>` ✔ `JsonString<Record<string, number>>` ✔ `JsonString<Record<string, number | undefined>>` ✔ `JsonString<bigint>` ✔ `JsonString<unknown>` supports literal types ✔ `true` ✔ `false` ✔ `0` ✔ "string" ✔ `null` ✔ object with literals ✔ array of literals ✔ tuple of literals ✔ specific numeric enum value ✔ specific string enum value ✔ specific const heterogenous enum value ✔ specific computed enum value supports array types ✔ array of `number`s ✔ readonly array of `number`s ✔ readonly array of simple objects supports object types ✔ empty object ✔ object with `never` ✔ object with `boolean` ... ✔ `string`|`number` indexed record of `string`s ✔ `string`|`number` indexed record of objects ✔ templated record of `numbers` ✔ `string` indexed record of `number`|`string`s with known properties ✔ `string`|`number` indexed record of `strings` with known `number` property (unassignable) ✔ branded-`string` indexed of `boolean`s ... ✔ object with possible type recursion through union ✔ object with optional type recursion ✔ object with deep type recursion ✔ object with alternating type recursion ✔ simple non-null object json (NonNullJsonObjectWith<never>) ✔ simple read-only non-null object json (ReadonlyNonNullJsonObjectWith<never>) ✔ non-const enums ✔ object with `readonly` ✔ object with getter implemented via value ✔ object with setter implemented via value ✔ object with matched getter and setter implemented via value ✔ object with mismatched getter and setter implemented via value class instance ✔ with public data (just cares about data) object with optional property ✔ without property ✔ with undefined value ✔ with defined value opaque Json types ✔ opaque serializable object ✔ opaque deserialized object ✔ opaque serializable and deserialized object ✔ opaque serializable unknown ✔ opaque deserialized unknown ✔ opaque serializable and deserialized unknown ✔ object with opaque serializable unknown ✔ object with opaque deserialized unknown ✔ recursive type with opaque serializable unknown ✔ recursive type with opaque deserialized unknown ✔ recursive type with opaque serializable and deserialized unknown ✔ recursive branded indexed object with OpaqueJsonDeserialized<unknown> supports union types ✔ simple json (JsonTypeWith<never>) ✔ simple read-only json (ReadonlyJsonTypeWith<never>) with NOT fully supported object types ✔ object with self reference throws on serialization known defect expectations ✔ sparse array of supported types ✔ object with sparse array of supported types getters and setters allowed but do not propagate ✔ object with `readonly` implemented via getter ✔ object with getter ✔ object with setter ✔ object with matched getter and setter ✔ object with mismatched getter and setter class instance with `ignore-inaccessible-members` ✔ with private data ignores private data (that propagates) invalid input usage assumptions ✔ const enums are never readable unsupported types cause compiler error ✔ `undefined` ✔ `unknown` ✔ `symbol` ✔ `unique symbol` ✔ `bigint` ✔ function ✔ function with supported properties ✔ object and function ✔ object with function with supported properties ✔ object with object and function ✔ function with class instance with private data ✔ function with class instance with public data ✔ class instance with private data and is function ✔ class instance with public data and is function ✔ `object` (plain object) ✔ `void` ✔ branded `object` ✔ branded object with `string` unions with unsupported primitive types ✔ `string | symbol` ✔ `bigint | string` ✔ `bigint | symbol` ✔ `number | bigint | symbol` array ✔ array of `bigint`s ✔ array of `symbol`s ... object ✔ object with exactly `bigint` ✔ object with optional `bigint` ✔ object with exactly `symbol` ... ✔ `string` indexed record of `unknown` ✔ `Partial<>` `string` indexed record of `unknown` ✔ `Partial<>` `string` indexed record of `numbers` ✔ `Partial<>` templated record of `numbers` ✔ object with recursion and `symbol` ✔ function object with recursion ✔ object and function with recursion ✔ nested function object with recursion ✔ nested object and function with recursion ✔ object with inherited recursion extended with unsupported properties object with `undefined` ✔ as exact property type ✔ in union property ... object with required `unknown` ✔ as exact property type ✔ as exact property type adjacent to recursion ✔ as exact property type in recursion of class instance ✔ with private data ✔ with private method ✔ with private getter ✔ with private setter ✔ with public method ✔ with private data in optional recursion opaque Json types requiring extra allowed types ✔ opaque serializable object with `bigint` ✔ opaque deserialized object with `bigint` ✔ opaque serializable and deserialized object with `bigint` ✔ opaque serializable object with number array expecting `bigint` support ✔ opaque deserialized object with number array expecting `bigint` support ✔ opaque serializable and deserialized object with number array expecting `bigint` support common class instances ✔ Map ✔ ReadonlyMap ✔ Set ✔ ReadonlySet Fluid types ✔ `IFluidHandle` ✔ object with `IFluidHandle` special cases ✔ explicit `any` generic still limits allowed types `number` edge cases supported ✔ MIN_SAFE_INTEGER ✔ MAX_SAFE_INTEGER ✔ MIN_VALUE ✔ MAX_VALUE resulting in `null` ✔ NaN ✔ +Infinity ✔ -Infinity JsonDeserialized positive compilation tests supported primitive types are preserved ✔ `JsonString<string>` ✔ `JsonString<{ arrayOfNumbers: number[] }>` ✔ `JsonString<Record<string, number>>` ✔ `JsonString<Record<string, number | undefined>>` ✔ JsonString<bigint> ✔ JsonString<unknown> JsonSerializable positive compilation tests supported primitive types ✔ `JsonString<string>` ✔ `JsonString<{ arrayOfNumbers: number[] }>` ✔ `JsonString<Record<string, number>>` ✔ `JsonString<Record<string, number | undefined>>` ✔ JsonString<bigint> ✔ JsonString<unknown> ```
1 parent 7611ba0 commit 4da8ea2

File tree

9 files changed

+3024
-40
lines changed

9 files changed

+3024
-40
lines changed

packages/common/core-interfaces/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@
3636
"./internal": {
3737
"import": {
3838
"types": "./lib/internal.d.ts",
39-
"default": "./lib/index.js"
39+
"default": "./lib/internal.js"
4040
},
4141
"require": {
4242
"types": "./dist/internal.d.ts",
43-
"default": "./dist/index.js"
43+
"default": "./dist/internal.js"
4444
}
4545
},
4646
"./internal/exposedUtilityTypes": {

packages/common/core-interfaces/src/cjs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
"./internal": {
1414
"types": "./internal.d.ts",
15-
"default": "./index.js"
15+
"default": "./internal.js"
1616
},
1717
"./internal/exposedUtilityTypes": "./exposedUtilityTypes.js"
1818
}

packages/common/core-interfaces/src/internal.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
// eslint-disable-next-line no-restricted-syntax, @typescript-eslint/no-restricted-imports
88
export * from "./index.js";
99

10-
// Important: all other exports must be type only exports. In package.json exports,
11-
// index.js is listed as the runtime file. This is done so that all imports are
12-
// using the same outer runtime file. (Could be changed if needed.)
10+
export type { JsonString, JsonStringifyOptions } from "./jsonString.js";
11+
export { JsonStringify, JsonParse } from "./jsonString.js";
1312

1413
export type { JsonTypeToOpaqueJson, OpaqueJsonToJsonType } from "./jsonUtils.js";
1514

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*!
2+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports -- incorrect rule: misunderstands `declare`d types.
7+
import type { BrandedType } from "./brandedType.js";
8+
import type { JsonDeserialized } from "./jsonDeserialized.js";
9+
import type { JsonSerializable } from "./jsonSerializable.js";
10+
11+
/**
12+
* Brand for JSON that has been stringified.
13+
*
14+
* Usage: Intersect with another type to apply branding.
15+
*
16+
* @sealed
17+
*/
18+
declare class JsonStringBrand<T> extends BrandedType<JsonString<unknown>> {
19+
public toString(): string;
20+
protected readonly EncodedValue: T;
21+
private constructor();
22+
}
23+
24+
/**
25+
* Branded `string` for JSON that has been stringified.
26+
*
27+
* @remarks
28+
*
29+
* Use {@link JsonStringify} to encode JSON producing values of this type and
30+
* {@link JsonParse} to decode them.
31+
*
32+
* For custom encoding/decoding:
33+
*
34+
* - cast to with `as unknown as JsonString<T>` when value of type `T` has been stringified.
35+
*
36+
* - use a form of {@link JsonDeserialized} for safety when parsing.
37+
*
38+
* @sealed
39+
* @internal
40+
*/
41+
export type JsonString<T> = string & JsonStringBrand<T>;
42+
43+
/**
44+
* Options for {@link JsonStringify}.
45+
*
46+
* @internal
47+
*/
48+
export interface JsonStringifyOptions {
49+
/**
50+
* When set, inaccessible (protected and private) members throughout type T are
51+
* ignored as if not present. Otherwise, inaccessible members are considered
52+
* an error (type checking will mention `SerializationErrorPerNonPublicProperties`).
53+
*
54+
* @remarks
55+
* The default is that `IgnoreInaccessibleMembers` property is not specified,
56+
* which means that inaccessible members are considered an error.
57+
*/
58+
IgnoreInaccessibleMembers?: "ignore-inaccessible-members";
59+
}
60+
61+
/**
62+
* Performs basic JSON serialization using `JSON.stringify` and brands the result as {@link JsonString}`<T>`.
63+
*
64+
* @remarks
65+
* Parameter `value` must be JSON-serializable and thus type T is put through filter {@link JsonSerializable}.
66+
*
67+
* @internal
68+
*/
69+
export const JsonStringify = JSON.stringify as <
70+
T,
71+
// eslint-disable-next-line @typescript-eslint/ban-types -- `Record<string, never>` is not sufficient replacement for empty object.
72+
Options extends JsonStringifyOptions = {},
73+
>(
74+
value: JsonSerializable<T, Options>,
75+
) => JsonString<T>;
76+
77+
/**
78+
* Performs basic JSON parsing using `JSON.parse` given a {@link JsonString}`<T>` (`string`).
79+
*
80+
* @remarks
81+
* Return type is filtered through {@link JsonDeserialized}`<T>` for best accuracy.
82+
*
83+
* @internal
84+
*/
85+
export const JsonParse: <T>(text: JsonString<T>) => JsonDeserialized<T> = JSON.parse;

packages/common/core-interfaces/src/test/jsonDeserialized.spec.ts

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import type {
2121
BrandedKey,
2222
BrandedString,
2323
DecodedValueDirectoryOrRequiredState,
24+
DeserializedOpaqueSerializableInRecursiveStructure,
25+
DeserializedOpaqueSerializableAndDeserializedInRecursiveStructure,
2426
DirectoryOfValues,
2527
ObjectWithOptionalRecursion,
2628
} from "./testValues.js";
@@ -211,6 +213,12 @@ import {
211213
opaqueSerializableObjectExpectingBigintSupport,
212214
opaqueDeserializedObjectExpectingBigintSupport,
213215
opaqueSerializableAndDeserializedObjectExpectingBigintSupport,
216+
jsonStringOfString,
217+
jsonStringOfObjectWithArrayOfNumbers,
218+
jsonStringOfStringRecordOfNumbers,
219+
jsonStringOfStringRecordOfNumberOrUndefined,
220+
jsonStringOfBigInt,
221+
jsonStringOfUnknown,
214222
} from "./testValues.js";
215223

216224
import type { IFluidHandle } from "@fluidframework/core-interfaces";
@@ -220,7 +228,6 @@ import type {
220228
JsonTypeWith,
221229
NonNullJsonObjectWith,
222230
OpaqueJsonDeserialized,
223-
OpaqueJsonSerializable,
224231
} from "@fluidframework/core-interfaces/internal/exposedUtilityTypes";
225232

226233
/**
@@ -368,6 +375,36 @@ describe("JsonDeserialized", () => {
368375
assertIdenticalTypes(resultRead, brandedString);
369376
assertNever<AnyLocations<typeof resultRead>>();
370377
});
378+
it("`JsonString<string>`", () => {
379+
const resultRead = passThru(jsonStringOfString);
380+
assertIdenticalTypes(resultRead, jsonStringOfString);
381+
assertNever<AnyLocations<typeof resultRead>>();
382+
});
383+
it("`JsonString<{ arrayOfNumbers: number[] }>`", () => {
384+
const resultRead = passThru(jsonStringOfObjectWithArrayOfNumbers);
385+
assertIdenticalTypes(resultRead, jsonStringOfObjectWithArrayOfNumbers);
386+
assertNever<AnyLocations<typeof resultRead>>();
387+
});
388+
it("`JsonString<Record<string, number>>`", () => {
389+
const resultRead = passThru(jsonStringOfStringRecordOfNumbers);
390+
assertIdenticalTypes(resultRead, jsonStringOfStringRecordOfNumbers);
391+
assertNever<AnyLocations<typeof resultRead>>();
392+
});
393+
it("`JsonString<Record<string, number | undefined>>`", () => {
394+
const resultRead = passThru(jsonStringOfStringRecordOfNumberOrUndefined);
395+
assertIdenticalTypes(resultRead, jsonStringOfStringRecordOfNumberOrUndefined);
396+
assertNever<AnyLocations<typeof resultRead>>();
397+
});
398+
it("JsonString<bigint>", () => {
399+
const resultRead = passThru(jsonStringOfBigInt);
400+
assertIdenticalTypes(resultRead, jsonStringOfBigInt);
401+
assertNever<AnyLocations<typeof resultRead>>();
402+
});
403+
it("JsonString<unknown>", () => {
404+
const resultRead = passThru(jsonStringOfUnknown);
405+
assertIdenticalTypes(resultRead, jsonStringOfUnknown);
406+
assertNever<AnyLocations<typeof resultRead>>();
407+
});
371408
});
372409

373410
describe("unions with unsupported primitive types preserve supported types", () => {
@@ -516,11 +553,15 @@ describe("JsonDeserialized", () => {
516553
});
517554
it("array of functions with properties becomes ({...}|null)[]", () => {
518555
const resultRead = passThru(arrayOfFunctionsWithProperties, [null]);
556+
// Note: a function with properties always results in `null`, but a property bag
557+
// with a function is the property bag. Allow either to avoid order-based logic.
519558
assertIdenticalTypes(resultRead, createInstanceOf<({ property: number } | null)[]>());
520559
assertNever<AnyLocations<typeof resultRead>>();
521560
});
522561
it("array of objects and functions becomes ({...}|null)[]", () => {
523562
const resultRead = passThru(arrayOfObjectAndFunctions, [{ property: 6 }]);
563+
// Note: a function with properties always results in `null`, but a property bag
564+
// with a function is the property bag. Allow either to avoid order-based logic.
524565
assertIdenticalTypes(resultRead, createInstanceOf<({ property: number } | null)[]>());
525566
assertNever<AnyLocations<typeof resultRead>>();
526567
});
@@ -1677,15 +1718,6 @@ describe("JsonDeserialized", () => {
16771718
});
16781719
it("object with OpaqueJsonSerializable<unknown> in recursion is unrolled one time with OpaqueJsonDeserialized", () => {
16791720
const resultRead = passThru(opaqueSerializableInRecursiveStructure);
1680-
interface DeserializedOpaqueSerializableInRecursiveStructure {
1681-
items: {
1682-
[x: string | number]:
1683-
| OpaqueJsonDeserialized<DirectoryOfValues<OpaqueJsonSerializable<unknown>>>
1684-
| {
1685-
value?: OpaqueJsonDeserialized<unknown>;
1686-
};
1687-
};
1688-
}
16891721
assertIdenticalTypes(
16901722
resultRead,
16911723
createInstanceOf<DeserializedOpaqueSerializableInRecursiveStructure>(),
@@ -1708,22 +1740,9 @@ describe("JsonDeserialized", () => {
17081740
// It might be better to preserve the intersection and return original type.
17091741
it("object with OpaqueJsonSerializable<unknown> & OpaqueJsonDeserialized<unknown> in recursion is unrolled one time with OpaqueJsonDeserialized", () => {
17101742
const resultRead = passThru(opaqueSerializableAndDeserializedInRecursiveStructure);
1711-
interface DeserializedOpaqueSerializableInRecursiveStructure {
1712-
items: {
1713-
[x: string | number]:
1714-
| OpaqueJsonDeserialized<
1715-
DirectoryOfValues<
1716-
OpaqueJsonSerializable<unknown> & OpaqueJsonDeserialized<unknown>
1717-
>
1718-
>
1719-
| {
1720-
value?: OpaqueJsonDeserialized<unknown>;
1721-
};
1722-
};
1723-
}
17241743
assertIdenticalTypes(
17251744
resultRead,
1726-
createInstanceOf<DeserializedOpaqueSerializableInRecursiveStructure>(),
1745+
createInstanceOf<DeserializedOpaqueSerializableAndDeserializedInRecursiveStructure>(),
17271746
);
17281747
assertNever<AnyLocations<typeof resultRead>>();
17291748
const transparentResult = revealOpaqueJson(resultRead);
@@ -1732,7 +1751,7 @@ describe("JsonDeserialized", () => {
17321751
createInstanceOf<{
17331752
items: {
17341753
[x: string | number]:
1735-
| DeserializedOpaqueSerializableInRecursiveStructure
1754+
| DeserializedOpaqueSerializableAndDeserializedInRecursiveStructure
17361755
| {
17371756
value?: JsonTypeWith<never>;
17381757
};

packages/common/core-interfaces/src/test/jsonSerializable.spec.ts

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,12 @@ import {
202202
opaqueSerializableObjectExpectingBigintSupport,
203203
opaqueDeserializedObjectExpectingBigintSupport,
204204
opaqueSerializableAndDeserializedObjectExpectingBigintSupport,
205+
jsonStringOfString,
206+
jsonStringOfObjectWithArrayOfNumbers,
207+
jsonStringOfStringRecordOfNumbers,
208+
jsonStringOfStringRecordOfNumberOrUndefined,
209+
jsonStringOfBigInt,
210+
jsonStringOfUnknown,
205211
} from "./testValues.js";
206212

207213
import type { IFluidHandle } from "@fluidframework/core-interfaces";
@@ -441,6 +447,36 @@ describe("JsonSerializable", () => {
441447
assertIdenticalTypes(filteredIn, brandedString);
442448
assertIdenticalTypes(filteredIn, out);
443449
});
450+
it("`JsonString<string>`", () => {
451+
const { filteredIn, out } = passThru(jsonStringOfString);
452+
assertIdenticalTypes(filteredIn, jsonStringOfString);
453+
assertIdenticalTypes(filteredIn, out);
454+
});
455+
it("`JsonString<{ arrayOfNumbers: number[] }>`", () => {
456+
const { filteredIn, out } = passThru(jsonStringOfObjectWithArrayOfNumbers);
457+
assertIdenticalTypes(filteredIn, jsonStringOfObjectWithArrayOfNumbers);
458+
assertIdenticalTypes(filteredIn, out);
459+
});
460+
it("`JsonString<Record<string, number>>`", () => {
461+
const { filteredIn, out } = passThru(jsonStringOfStringRecordOfNumbers);
462+
assertIdenticalTypes(filteredIn, jsonStringOfStringRecordOfNumbers);
463+
assertIdenticalTypes(filteredIn, out);
464+
});
465+
it("`JsonString<Record<string, number | undefined>>`", () => {
466+
const { filteredIn, out } = passThru(jsonStringOfStringRecordOfNumberOrUndefined);
467+
assertIdenticalTypes(filteredIn, jsonStringOfStringRecordOfNumberOrUndefined);
468+
assertIdenticalTypes(filteredIn, out);
469+
});
470+
it("JsonString<bigint>", () => {
471+
const { filteredIn, out } = passThru(jsonStringOfBigInt);
472+
assertIdenticalTypes(filteredIn, jsonStringOfBigInt);
473+
assertIdenticalTypes(filteredIn, out);
474+
});
475+
it("JsonString<unknown>", () => {
476+
const { filteredIn, out } = passThru(jsonStringOfUnknown);
477+
assertIdenticalTypes(filteredIn, jsonStringOfUnknown);
478+
assertIdenticalTypes(filteredIn, out);
479+
});
444480
});
445481

446482
describe("supported literal types", () => {
@@ -1566,8 +1602,10 @@ describe("JsonSerializable", () => {
15661602
});
15671603

15681604
it("`string` indexed record of `unknown`", () => {
1569-
// @ts-expect-error not assignable to parameter of type '{ [x: string]: JsonTypeWith<never> | OpaqueJsonSerializable<unknown>; }'.
1570-
const { filteredIn } = passThru(stringRecordOfUnknown);
1605+
const { filteredIn } = passThru(
1606+
// @ts-expect-error not assignable to parameter of type '{ [x: string]: JsonTypeWith<never> | OpaqueJsonSerializable<unknown>; }'.
1607+
stringRecordOfUnknown,
1608+
);
15711609
assertIdenticalTypes(
15721610
filteredIn,
15731611
createInstanceOf<{
@@ -1576,8 +1614,10 @@ describe("JsonSerializable", () => {
15761614
);
15771615
});
15781616
it("`Partial<>` `string` indexed record of `unknown`", () => {
1579-
// @ts-expect-error not assignable to parameter of type '{ [x: string]: JsonTypeWith<never> | OpaqueJsonSerializable<unknown>; }'.
1580-
const { filteredIn } = passThru(partialStringRecordOfUnknown);
1617+
const { filteredIn } = passThru(
1618+
// @ts-expect-error not assignable to parameter of type '{ [x: string]: JsonTypeWith<never> | OpaqueJsonSerializable<unknown>; }'.
1619+
partialStringRecordOfUnknown,
1620+
);
15811621
assertIdenticalTypes(
15821622
filteredIn,
15831623
createInstanceOf<{
@@ -1593,8 +1633,11 @@ describe("JsonSerializable", () => {
15931633
// Allowing `undefined` is possible if all indexed properties are
15941634
// identifiable. But rather than that, an implementation of `Partial<>`
15951635
// that doesn't add `| undefined` for index signatures would be preferred.
1596-
// @ts-expect-error not assignable to type '{ "error required property may not allow `undefined` value": never; }'
1597-
const { filteredIn } = passThru(partialStringRecordOfNumbers, { key1: 0 });
1636+
const { filteredIn } = passThru(
1637+
// @ts-expect-error not assignable to type '{ "error required property may not allow `undefined` value": never; }'
1638+
partialStringRecordOfNumbers,
1639+
{ key1: 0 },
1640+
);
15981641
assertIdenticalTypes(
15991642
filteredIn,
16001643
createInstanceOf<{
@@ -1611,8 +1654,11 @@ describe("JsonSerializable", () => {
16111654
// Allowing `undefined` is possible if all indexed properties are
16121655
// identifiable. But rather than that, an implementation of `Partial<>`
16131656
// that doesn't add `| undefined` for index signatures would be preferred.
1614-
// @ts-expect-error not assignable to type '{ "error required property may not allow `undefined` value": never; }'
1615-
const { filteredIn } = passThru(partialTemplatedRecordOfNumbers, { key1: 0 });
1657+
const { filteredIn } = passThru(
1658+
// @ts-expect-error not assignable to type '{ "error required property may not allow `undefined` value": never; }'
1659+
partialTemplatedRecordOfNumbers,
1660+
{ key1: 0 },
1661+
);
16161662
assertIdenticalTypes(
16171663
filteredIn,
16181664
createInstanceOf<{

0 commit comments

Comments
 (0)