Last active
June 24, 2022 11:35
-
-
Save frontsideair/dfd696bc927514ba4a78e1c5ed4acd1c to your computer and use it in GitHub Desktop.
Jsonify TypeScript helper type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// based on this blog: https://effectivetypescript.com/2020/04/09/jsonify/ | |
type Invalid = undefined | Function | symbol; | |
export type Jsonify<T> = T extends { toJSON(): infer U } | |
? U | |
: T extends BigInt | Invalid | |
? never | |
: T extends Number | |
? number | |
: T extends String | |
? string | |
: T extends Boolean | |
? boolean | |
: T extends Array<infer v> | |
? Array<Jsonify<v extends Invalid ? null : v>> | |
: T extends object | |
? { | |
[k in keyof T as k extends symbol | |
? never | |
: T[k] extends Invalid | |
? never | |
: k]: Jsonify<T[k]>; | |
} | |
: T; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { test } from "vitest"; | |
import type { TypeEqual } from "ts-expect"; | |
import { expectType } from "ts-expect"; | |
import type { Jsonify } from "./jsonify"; | |
test("jsonify works", () => { | |
function jsonRoundTrip<T>(x: T): Jsonify<T> { | |
return JSON.parse(JSON.stringify(x)); | |
} | |
const o = { | |
x: 5, | |
y: 6, | |
toJSON() { | |
return this.x + this.y; | |
}, | |
}; | |
expectType<number>(jsonRoundTrip(o)); | |
expectType<TypeEqual<never, Jsonify<bigint>>>(true); | |
const bucket = { | |
n: new Number(3), | |
s: new String("false"), | |
b: new Boolean(false), | |
}; | |
expectType<{ n: number; s: string; b: boolean }>(jsonRoundTrip(bucket)); | |
const u = undefined; | |
expectType<TypeEqual<never, Jsonify<undefined>>>(true); | |
const f = () => {}; | |
expectType<TypeEqual<never, Jsonify<Function>>>(true); | |
const s: symbol = Symbol.for("hello"); | |
expectType<TypeEqual<never, Jsonify<symbol>>>(true); | |
const obj = { u, f, s, keep: "this" }; | |
expectType<{ keep: string }>(jsonRoundTrip(obj)); | |
const x7 = jsonRoundTrip([u]); | |
expectType<Array<null>>(x7); | |
const x8 = jsonRoundTrip([f]); | |
expectType<Array<null>>(x8); | |
const x9 = jsonRoundTrip([s]); | |
expectType<Array<null>>(x9); | |
const x10 = jsonRoundTrip({ [s]: "hello", keep: "this" }); | |
expectType<{ keep: string }>(x10); | |
// NOTE: this can't work with TS 4.7.2 or earlier | |
// const x11 = jsonRoundTrip(Infinity); | |
// expectType<null>(x11); | |
// NOTE: this can't work with TS 4.7.2 or earlier | |
// const x12 = jsonRoundTrip(NaN); | |
// expectType<null>(x12); | |
const x13 = jsonRoundTrip(null); | |
expectType<null>(x13); | |
// const x14 = jsonRoundTrip([ | |
// new Int8Array([1]), | |
// new Int16Array([1]), | |
// new Int32Array([1]), | |
// ]); | |
// NOTE: what should happen here? | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment