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