Skip to content

Commit

Permalink
Jsonify: Handle undefined in array (#310)
Browse files Browse the repository at this point in the history
  • Loading branch information
darcyparker committed Mar 30, 2022
1 parent 8379e6d commit 17b0235
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 1 deletion.
9 changes: 8 additions & 1 deletion source/jsonify.d.ts
Expand Up @@ -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> =
T extends undefined ?
null | Exclude<T, undefined> :
Jsonify<T>;

/**
Transform a type to one that is assignable to the `JsonValue` type.
Expand Down Expand Up @@ -64,7 +71,7 @@ type Jsonify<T> =
? T extends JsonPrimitive
? T // Primitive is acceptable
: T extends Array<infer U>
? Array<Jsonify<U>> // It's an array: recursive call for its children
? Array<JsonifyArrayMember<U>> // 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?
Expand Down
12 changes: 12 additions & 0 deletions test-d/jsonify.ts
Expand Up @@ -123,6 +123,18 @@ const nonJsonWithInvalidToJSON = new NonJsonWithInvalidToJSON();
expectNotAssignable<JsonValue>(nonJsonWithInvalidToJSON);
expectNotAssignable<JsonValue>(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<JsonValue>([undefined]);
declare const parsedStringifiedArrayWithUndefined1: Jsonify<undefined[]>; // = JSON.parse(JSON.stringify([undefined]));
expectType<null[]>(parsedStringifiedArrayWithUndefined1);
expectAssignable<JsonValue>(parsedStringifiedArrayWithUndefined1);
expectNotAssignable<JsonValue>([undefined, 1]);
declare const parsedStringifiedArrayWithUndefined2: Jsonify<[undefined, number]>;
expectType<Array<number | null>>(parsedStringifiedArrayWithUndefined2);
expectAssignable<JsonValue>(parsedStringifiedArrayWithUndefined2);

// Test that optional type members are not discarded wholesale.
interface OptionalPrimitive {
a?: string;
Expand Down

0 comments on commit 17b0235

Please sign in to comment.