frontend/.pnpm-store/v3/files/49/36993722d69a0b9210e4d1b9fb894d83ab4e19f8b9b50eaab1de884160c98a2e1338ac84acd445b6619243550eb44d3aa293afab138d1344c655461758c318

115 lines
3.8 KiB
Plaintext

import type {JsonPrimitive, JsonValue} from './basic';
import type {EmptyObject} from './empty-object';
import type {IsAny, UndefinedToOptional} from './internal';
import type {NegativeInfinity, PositiveInfinity} from './numeric';
import type {TypedArray} from './typed-array';
// Note: The return value has to be `any` and not `unknown` so it can match `void`.
type NotJsonable = ((...arguments_: any[]) => any) | undefined | symbol;
type JsonifyTuple<T extends [unknown, ...unknown[]]> = {
[Key in keyof T]: T[Key] extends NotJsonable ? null : Jsonify<T[Key]>;
};
type FilterJsonableKeys<T extends object> = {
[Key in keyof T]: T[Key] extends NotJsonable ? never : Key;
}[keyof T];
/**
JSON serialize objects (not including arrays) and classes.
*/
type JsonifyObject<T extends object> = {
[Key in keyof Pick<T, FilterJsonableKeys<T>>]: Jsonify<T[Key]>;
};
/**
Transform a type to one that is assignable to the `JsonValue` type.
This includes:
1. Transforming JSON `interface` to a `type` that is assignable to `JsonValue`.
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`.
@remarks
An interface cannot be structurally compared to `JsonValue` because an interface can be re-opened to add properties that may not be satisfy `JsonValue`.
@example
```
import type {Jsonify, JsonValue} from 'type-fest';
interface Geometry {
type: 'Point' | 'Polygon';
coordinates: [number, number];
}
const point: Geometry = {
type: 'Point',
coordinates: [1, 1]
};
const problemFn = (data: JsonValue) => {
// Does something with data
};
problemFn(point); // Error: type Geometry is not assignable to parameter of type JsonValue because it is an interface
const fixedFn = <T>(data: Jsonify<T>) => {
// Does something with data
};
fixedFn(point); // Good: point is assignable. Jsonify<T> transforms Geometry into value assignable to JsonValue
fixedFn(new Date()); // Error: As expected, Date is not assignable. Jsonify<T> cannot transforms Date into value assignable to JsonValue
```
Non-JSON values such as `Date` implement `.toJSON()`, so they can be transformed to a value assignable to `JsonValue`:
@example
```
import type {Jsonify} from 'type-fest';
const time = {
timeValue: new Date()
};
// `Jsonify<typeof time>` is equivalent to `{timeValue: string}`
const timeJson = JSON.parse(JSON.stringify(time)) as Jsonify<typeof time>;
```
@link https://github.com/Microsoft/TypeScript/issues/1897#issuecomment-710744173
@category JSON
*/
export type Jsonify<T> = IsAny<T> extends true
? any
: T extends PositiveInfinity | NegativeInfinity
? null
: T extends JsonPrimitive
? T
: // Instanced primitives are objects
T extends Number
? number
: T extends String
? string
: T extends Boolean
? boolean
: T extends Map<any, any> | Set<any>
? EmptyObject
: T extends TypedArray
? Record<string, number>
: T extends NotJsonable
? never // Non-JSONable type union was found not empty
: // 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
: Jsonify<J> // Maybe if we look a level deeper we'll find a JsonValue
: T extends []
? []
: T extends [unknown, ...unknown[]]
? JsonifyTuple<T>
: T extends ReadonlyArray<infer U>
? Array<U extends NotJsonable ? null : Jsonify<U>>
: T extends object
? JsonifyObject<UndefinedToOptional<T>> // JsonifyObject recursive call for its children
: never; // Otherwise any other non-object is removed