diff --git a/source/jsonify.d.ts b/source/jsonify.d.ts index 10bdf5e0b..20c03a7c7 100644 --- a/source/jsonify.d.ts +++ b/source/jsonify.d.ts @@ -3,6 +3,13 @@ import {JsonPrimitive, JsonValue} from './basic'; // Note: The return value has to be `any` and not `unknown` so it can match `void`. type NotJsonable = ((...args: any[]) => any) | undefined; +// Note: Handles special case where Arrays with `undefined` are transformed to `'null'` by `JSON.stringify()` +// Only use with array members +type JsonifyArrayMember = + T extends undefined ? + null | Exclude : + Jsonify; + /** Transform a type to one that is assignable to the `JsonValue` type. @@ -64,7 +71,7 @@ type Jsonify = ? T extends JsonPrimitive ? T // Primitive is acceptable : T extends Array - ? Array> // It's an array: recursive call for its children + ? Array> // It's an array: recursive call for its children : T extends object ? T extends {toJSON(): infer J} ? (() => J) extends (() => JsonValue) // Is J assignable to JsonValue? diff --git a/test-d/jsonify.ts b/test-d/jsonify.ts index ee467fc58..dd51f51cb 100644 --- a/test-d/jsonify.ts +++ b/test-d/jsonify.ts @@ -123,6 +123,18 @@ const nonJsonWithInvalidToJSON = new NonJsonWithInvalidToJSON(); expectNotAssignable(nonJsonWithInvalidToJSON); expectNotAssignable(nonJsonWithInvalidToJSON.toJSON()); +// Special cases of Array with `undefined` member +// `[undefined]` is not JSON because it contains non JSON value `undefined` +// However `JSON.parse(JSON.stringify())` transforms array members of `undefined` to `null` +expectNotAssignable([undefined]); +declare const parsedStringifiedArrayWithUndefined1: Jsonify; // = JSON.parse(JSON.stringify([undefined])); +expectType(parsedStringifiedArrayWithUndefined1); +expectAssignable(parsedStringifiedArrayWithUndefined1); +expectNotAssignable([undefined, 1]); +declare const parsedStringifiedArrayWithUndefined2: Jsonify<[undefined, number]>; +expectType>(parsedStringifiedArrayWithUndefined2); +expectAssignable(parsedStringifiedArrayWithUndefined2); + // Test that optional type members are not discarded wholesale. interface OptionalPrimitive { a?: string;