Mirror of https://github.com/roostorg/coop
github.com/roostorg/coop
1import { type Opaque } from 'type-fest';
2
3export type NonEmptyArray<T> = [T, ...T[]];
4
5export type ReadonlyDeep<T> = T extends object
6 ? { readonly [P in keyof T]: ReadonlyDeep<T[P]> }
7 : T;
8
9/**
10 * Converts a value to JSON, while preserving its type for future inspection.
11 *
12 * Sometimes, we need to stringify a value (e.g., to use the string as a key),
13 * but we'd still like to Typescript to track the original type that we
14 * stringified, so that we can have type checking on the data we'll get back
15 * if/when we JSON.parse the string later. That's what this `jsonStringify`
16 * helper function does. See {@link jsonParse}
17 */
18export function jsonStringify<T>(it: T) {
19 return JSON.stringify(it) as JsonOf<T>;
20}
21
22/**
23 * Parses the JSON, and returns its original type, for JSON generated by
24 * {@link jsonStringify}.
25 */
26export function jsonParse<T extends JsonOf<unknown>>(it: T) {
27 return JSON.parse(it) as (typeof it)[typeof meta];
28}
29
30declare const meta: unique symbol;
31export type JsonOf<T> = Opaque<string, 'JSON'> & { [meta]: T };
32
33// A convenient helper for building types that have different values based on
34// some boolean generic.
35export type If<Cond extends boolean, IfTrue, IfFalse> = Cond extends true
36 ? IfTrue
37 : Cond extends false
38 ? IfFalse
39 : IfTrue | IfFalse;