|
| 1 | +/** |
| 2 | + * @see https://github.com/sindresorhus/type-fest/blob/main/source/jsonify.d.ts |
| 3 | + */ |
| 4 | + |
| 5 | +declare const emptyObjectSymbol: unique symbol; |
| 6 | +type EmptyObject = { [emptyObjectSymbol]?: never }; |
| 7 | + |
| 8 | +type IsAny<T> = 0 extends 1 & T ? true : false; |
| 9 | + |
| 10 | +type JsonArray = JsonValue[]; |
| 11 | +type JsonObject = { [Key in string]: JsonValue } & { |
| 12 | + [Key in string]?: JsonValue | undefined; |
| 13 | +}; |
| 14 | +type JsonPrimitive = string | number | boolean | null; |
| 15 | +type JsonValue = JsonPrimitive | JsonObject | JsonArray; |
| 16 | + |
| 17 | +type NegativeInfinity = -1e999; |
| 18 | +type PositiveInfinity = 1e999; |
| 19 | + |
| 20 | +type TypedArray = |
| 21 | + | Int8Array |
| 22 | + | Uint8Array |
| 23 | + | Uint8ClampedArray |
| 24 | + | Int16Array |
| 25 | + | Uint16Array |
| 26 | + | Int32Array |
| 27 | + | Uint32Array |
| 28 | + | Float32Array |
| 29 | + | Float64Array |
| 30 | + | BigInt64Array |
| 31 | + | BigUint64Array; |
| 32 | + |
| 33 | +type BaseKeyFilter<Type, Key extends keyof Type> = Key extends symbol |
| 34 | + ? never |
| 35 | + : Type[Key] extends symbol |
| 36 | + ? never |
| 37 | + : [(...args: any[]) => any] extends [Type[Key]] |
| 38 | + ? never |
| 39 | + : Key; |
| 40 | +type FilterDefinedKeys<T extends object> = Exclude< |
| 41 | + { |
| 42 | + [Key in keyof T]: IsAny<T[Key]> extends true |
| 43 | + ? Key |
| 44 | + : undefined extends T[Key] |
| 45 | + ? never |
| 46 | + : T[Key] extends undefined |
| 47 | + ? never |
| 48 | + : BaseKeyFilter<T, Key>; |
| 49 | + }[keyof T], |
| 50 | + undefined |
| 51 | +>; |
| 52 | +type FilterOptionalKeys<T extends object> = Exclude< |
| 53 | + { |
| 54 | + [Key in keyof T]: IsAny<T[Key]> extends true |
| 55 | + ? never |
| 56 | + : undefined extends T[Key] |
| 57 | + ? T[Key] extends undefined |
| 58 | + ? never |
| 59 | + : BaseKeyFilter<T, Key> |
| 60 | + : never; |
| 61 | + }[keyof T], |
| 62 | + undefined |
| 63 | +>; |
| 64 | +type UndefinedToOptional<T extends object> = { |
| 65 | + // Property is not a union with `undefined`, keep it as-is. |
| 66 | + [Key in keyof Pick<T, FilterDefinedKeys<T>>]: T[Key]; |
| 67 | +} & { |
| 68 | + // Property _is_ a union with defined value. Set as optional (via `?`) and remove `undefined` from the union. |
| 69 | + [Key in keyof Pick<T, FilterOptionalKeys<T>>]?: Exclude<T[Key], undefined>; |
| 70 | +}; |
| 71 | + |
| 72 | +// Note: The return value has to be `any` and not `unknown` so it can match `void`. |
| 73 | +type NotJsonable = ((...args: any[]) => any) | undefined | symbol; |
| 74 | + |
| 75 | +type JsonifyTuple<T extends [unknown, ...unknown[]]> = { |
| 76 | + [Key in keyof T]: T[Key] extends NotJsonable ? null : Jsonify<T[Key]>; |
| 77 | +}; |
| 78 | + |
| 79 | +type FilterJsonableKeys<T extends object> = { |
| 80 | + [Key in keyof T]: T[Key] extends NotJsonable ? never : Key; |
| 81 | +}[keyof T]; |
| 82 | + |
| 83 | +type JsonifyObject<T extends object> = { |
| 84 | + [Key in keyof Pick<T, FilterJsonableKeys<T>>]: Jsonify<T[Key]>; |
| 85 | +}; |
| 86 | + |
| 87 | +// prettier-ignore |
| 88 | +export type Jsonify<T> = |
| 89 | + IsAny<T> extends true ? any |
| 90 | + : T extends PositiveInfinity | NegativeInfinity ? null |
| 91 | + : T extends JsonPrimitive ? T |
| 92 | + // Instanced primitives are objects |
| 93 | + : T extends Number ? number |
| 94 | + : T extends String ? string |
| 95 | + : T extends Boolean ? boolean |
| 96 | + : T extends Map<any, any> | Set<any> ? EmptyObject |
| 97 | + : T extends TypedArray ? Record<string, number> |
| 98 | + : T extends NotJsonable ? never // Non-JSONable type union was found not empty |
| 99 | + // Any object with toJSON is special case |
| 100 | + : T extends { toJSON(): infer J } ? |
| 101 | + (() => J) extends () => JsonValue // Is J assignable to JsonValue? |
| 102 | + ? J // Then T is Jsonable and its Jsonable value is J |
| 103 | + : Jsonify<J> // Maybe if we look a level deeper we'll find a JsonValue |
| 104 | + : T extends [] ? [] |
| 105 | + : T extends [unknown, ...unknown[]] ? JsonifyTuple<T> |
| 106 | + : T extends ReadonlyArray<infer U> ? Array<U extends NotJsonable ? null : Jsonify<U>> |
| 107 | + : T extends object ? JsonifyObject<UndefinedToOptional<T>> // JsonifyObject recursive call for its children |
| 108 | + : never; // Otherwise any other non-object is removed |
0 commit comments