Skip to content

Instantly share code, notes, and snippets.

@dfoverdx
Last active January 25, 2022 19:23
Show Gist options
  • Save dfoverdx/99b5789b30ffd09317fcba6c48a768e0 to your computer and use it in GitHub Desktop.
Save dfoverdx/99b5789b30ffd09317fcba6c48a768e0 to your computer and use it in GitHub Desktop.
Inferring the type of JSON.parse(JSON.stringify(x))

A little TypeScript fun

Sometimes I get bored at work, and so I mess around with the TypeScript typing engine. And sometimes I use JSON.parse(JSON.stringify(x)) as a quick and dirty deep clone (not performant, mind you). Rather than getting any, I want to infer the shape of the result.

So, here ya go. Enjoy. TS Playground

type Func = (...args: any) => any;
type PropNamesOfType<T, U> = { [K in keyof T]: T[K] extends U ? K : never; }[keyof T];
type JSONPrimitive = string | null | number | boolean;
type NonJSONPrimitive = Func | symbol | undefined;
declare const __JSON_PARSABLE_DUMMY_SYMBOL__: unique symbol;
type JSONParsable<T> = string & { [__JSON_PARSABLE_DUMMY_SYMBOL__]: T };
/**
* Determines the shape of the following:
* ```
* x => JSON.parse(JSON.stringify(x))
* ```
*/
type ToJSON<T> =
T extends { toJSON(): infer U } ? U :
T extends JSONPrimitive ? T :
T extends (infer U)[] ? ToJSON<U>[] :
T extends NonJSONPrimitive ? undefined :
{ [K in Exclude<keyof T, PropNamesOfType<T, NonJSONPrimitive>>]: ToJSON<T[K]> };
interface JSON {
stringify<T>(
value: T,
replacer?: (this: any, key: string, value: any) => any, space?: string | number
): T extends NonJSONPrimitive ? undefined : JSONParsable<T>;
stringify<T>(
value: T,
replacer?: (number | string)[] | null,
space?: string | number
): T extends NonJSONPrimitive ? undefined : JSONParsable<T>;
parse<T>(value: JSONParsable<T>, reviver?: (this: any, key: string, value: any) => any): ToJSON<T>;
}
const x = {
a: false,
b: (x: any) => x,
c: [ 1, 2, 3 ],
d: {
da: true,
db: (x: any) => x,
dc: ['a', 'b', 'c'],
},
e: Symbol(),
f: undefined,
g: null,
}
const y = JSON.parse(JSON.stringify(x));
y.a = true;
// @ts-expect-error
y.a = 1;
// @ts-expect-error
y.b;
y.d.dc = ['x', 'y', 'z'];
// @ts-expect-error
y.d.dc = [1, 2, 3];
// @ts-expect-error
JSON.parse(JSON.stringify(() => {}))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment