1 | import type {JsonPrimitive, JsonValue} from './basic';
|
2 | import type {EmptyObject} from './empty-object';
|
3 | import type {UndefinedToOptional} from './internal';
|
4 | import type {IsAny} from './is-any';
|
5 | import type {IsNever} from './is-never';
|
6 | import type {IsUnknown} from './is-unknown';
|
7 | import type {NegativeInfinity, PositiveInfinity} from './numeric';
|
8 | import type {TypedArray} from './typed-array';
|
9 | import type {UnknownArray} from './unknown-array';
|
10 |
|
11 |
|
12 | type NotJsonable = ((...arguments_: any[]) => any) | undefined | symbol;
|
13 |
|
14 | type NeverToNull<T> = IsNever<T> extends true ? null : T;
|
15 |
|
16 | // Handles tuples and arrays
|
17 | type JsonifyList<T extends UnknownArray> = T extends readonly []
|
18 | ? []
|
19 | : T extends readonly [infer F, ...infer R]
|
20 | ? [NeverToNull<Jsonify<F>>, ...JsonifyList<R>]
|
21 | : IsUnknown<T[number]> extends true
|
22 | ? []
|
23 | : Array<T[number] extends NotJsonable ? null : Jsonify<T[number]>>;
|
24 |
|
25 | type FilterJsonableKeys<T extends object> = {
|
26 | [Key in keyof T]: T[Key] extends NotJsonable ? never : Key;
|
27 | }[keyof T];
|
28 |
|
29 | /**
|
30 | JSON serialize objects (not including arrays) and classes.
|
31 | */
|
32 | type JsonifyObject<T extends object> = {
|
33 | [Key in keyof Pick<T, FilterJsonableKeys<T>>]: Jsonify<T[Key]>;
|
34 | };
|
35 |
|
36 | /**
|
37 | Transform a type to one that is assignable to the `JsonValue` type.
|
38 |
|
39 | This includes:
|
40 | 1. Transforming JSON `interface` to a `type` that is assignable to `JsonValue`.
|
41 | 2. Transforming non-JSON value that is *jsonable* to a type that is assignable to `JsonValue`, where *jsonable* means the non-JSON value implements the `.toJSON()` method that returns a value that is assignable to `JsonValue`.
|
42 |
|
43 | @remarks
|
44 |
|
45 | An interface cannot be structurally compared to `JsonValue` because an interface can be re-opened to add properties that may not be satisfy `JsonValue`.
|
46 |
|
47 | @example
|
48 | ```
|
49 | import type {Jsonify, JsonValue} from 'type-fest';
|
50 |
|
51 | interface Geometry {
|
52 | type: 'Point' | 'Polygon';
|
53 | coordinates: [number, number];
|
54 | }
|
55 |
|
56 | const point: Geometry = {
|
57 | type: 'Point',
|
58 | coordinates: [1, 1]
|
59 | };
|
60 |
|
61 | const problemFn = (data: JsonValue) => {
|
62 |
|
63 | };
|
64 |
|
65 | problemFn(point);
|
66 |
|
67 | const fixedFn = <T>(data: Jsonify<T>) => {
|
68 |
|
69 | };
|
70 |
|
71 | fixedFn(point);
|
72 | fixedFn(new Date());
|
73 | ```
|
74 |
|
75 | Non-JSON values such as `Date` implement `.toJSON()`, so they can be transformed to a value assignable to `JsonValue`:
|
76 |
|
77 | @example
|
78 | ```
|
79 | import type {Jsonify} from 'type-fest';
|
80 |
|
81 | const time = {
|
82 | timeValue: new Date()
|
83 | };
|
84 |
|
85 |
|
86 | const timeJson = JSON.parse(JSON.stringify(time)) as Jsonify<typeof time>;
|
87 | ```
|
88 |
|
89 | @link https://github.com/Microsoft/TypeScript/issues/1897#issuecomment-710744173
|
90 |
|
91 | @category JSON
|
92 | */
|
93 | export type Jsonify<T> = IsAny<T> extends true
|
94 | ? any
|
95 | : T extends PositiveInfinity | NegativeInfinity
|
96 | ? null
|
97 | : T extends JsonPrimitive
|
98 | ? T
|
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 | : // Instanced primitives are objects
|
105 | T extends Number
|
106 | ? number
|
107 | : T extends String
|
108 | ? string
|
109 | : T extends Boolean
|
110 | ? boolean
|
111 | : T extends Map<any, any> | Set<any>
|
112 | ? EmptyObject
|
113 | : T extends TypedArray
|
114 | ? Record<string, number>
|
115 | : T extends NotJsonable
|
116 | ? never // Non-JSONable type union was found not empty
|
117 | : T extends UnknownArray
|
118 | ? JsonifyList<T>
|
119 | : T extends object
|
120 | ? JsonifyObject<UndefinedToOptional<T>> // JsonifyObject recursive call for its children
|
121 | : never; // Otherwise any other non-object is removed
|
122 |
|
\ | No newline at end of file |