Skip to content

Commit

Permalink
Jsonify: Do not downgrade tuples to arrays (#433)
Browse files Browse the repository at this point in the history
  • Loading branch information
qwelias committed Aug 18, 2022
1 parent 96acab1 commit 8a014e5
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 21 deletions.
41 changes: 20 additions & 21 deletions source/jsonify.d.ts
Expand Up @@ -67,25 +67,24 @@ type Jsonify<T> =
// Note: The use of tuples in this first condition side-steps distributive conditional types
// (see https://github.com/microsoft/TypeScript/issues/29368#issuecomment-453529532)
[Extract<T, NotJsonable | bigint>] extends [never]
? T extends PositiveInfinity | NegativeInfinity ? null
: T extends JsonPrimitive
? T // Primitive is acceptable
: T extends Number ? number
: T extends String ? string
: T extends Boolean ? boolean
: T extends Map<any, any> | Set<any> ? {}
: T extends TypedArray ? Record<string, number>
: T extends Array<infer U>
? Array<Jsonify<U extends NotJsonable ? null : 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?
? J // Then T is Jsonable and its Jsonable value is J
: never // Not Jsonable because its toJSON() method does not return JsonValue
: {[P in keyof T as P extends symbol
? never
: T[P] extends NotJsonable
? never
: P]: Jsonify<Required<T>[P]>} // It's an object: recursive call for its children
: never // Otherwise any other non-object is removed
? T extends PositiveInfinity | NegativeInfinity ? null
: T extends JsonPrimitive ? T // Primitive is acceptable
: T extends object
// Any object with toJSON is special case
? T extends {toJSON(): infer J} ? (() => J) extends (() => JsonValue) // Is J assignable to JsonValue?
? J // Then T is Jsonable and its Jsonable value is J
: never // Not Jsonable because its toJSON() method does not return JsonValue
// Instanced primitives are objects
: T extends Number ? number
: T extends String ? string
: T extends Boolean ? boolean
: T extends Map<any, any> | Set<any> ? {}
: T extends TypedArray ? Record<string, number>
: T extends any[]
? {[I in keyof T]: T[I] extends NotJsonable ? null : Jsonify<T[I]>}
: {[P in keyof T as P extends symbol ? never
: T[P] extends NotJsonable ? never
: P
]: Jsonify<Required<T>[P]>} // Recursive call for its children
: never // Otherwise any other non-object is removed
: never; // Otherwise non-JSONable type union was found not empty
6 changes: 6 additions & 0 deletions test-d/jsonify.ts
Expand Up @@ -186,6 +186,12 @@ expectType<string>(stringJson);
declare const booleanJson: Jsonify<typeof boolean>;
expectType<boolean>(booleanJson);

declare const tupleJson: Jsonify<[string, Date]>;
expectType<[string, string]>(tupleJson);

declare const tupleRestJson: Jsonify<[string, ...Date[]]>;
expectType<[string, ...string[]]>(tupleRestJson);

// BigInt fails JSON.stringify
declare const bigInt: Jsonify<bigint>;
expectType<never>(bigInt);
Expand Down

0 comments on commit 8a014e5

Please sign in to comment.