JSON化したあとの型を取得する
JSON.stringify()すると、データに変化が起きることがある。
例えばDateはstringになる。
DateにtoJSON()が実装されているため。
node> JSON.stringify(new Date());"2022-01-20T12:31:51.531Z"JSON.stringify()の仕様をもとに、JSON.stringify()してJSON.parse()したあとのTypeScript型を導出してみた。
JSONCompatible<Type>で、JSON化したあとの型を取得できる。
test.tstype Test = JSONCompatible<{ date: Date; string: string;}>;
制限事項
InfinityやNaNを正しく判定できません。
仕様では、InfinityやNaNをJSON.stringify()するとnullになる。
しかし、
JSONCompatible<typeof Infinity | typeof NaN> はnumberとして判定される。 TypeScriptではInfinityやNaNを判定できない。
JavaScriptで動的に判定するしかない。
ソースコード
JSONCompatible.tsexport type JSONCompatible<Target> = Target extends { toJSON: (...args: any) => any;} ? JSONCompatible<ReturnType<Target["toJSON"]>> : // Infinity and NaN are not supported. Target extends boolean | null | number | string ? Target : Target extends ((...args: any) => any) | symbol | undefined ? never : Target extends Record<string, any> ? { [Key in keyof Target]: JSONCompatible<Target[Key]> } : unknown;
JSONCompatible.ts// Tests{ { const target = { object: { // Primitive types boolean: true, null: null, number: 1, string: "string", // Object types array: [true, [1]], date: new Date(), // Unsupported types infinity: Infinity, nan: NaN, }, } as const;
const typeCheck: { object: { // Primitive types boolean: true; null: null; number: 1; string: "string"; // Object types array: readonly [true, readonly [1]]; date: string; // Date.toJSON() returns string // Unsupported types infinity: number; // null is the correct type nan: number; // null is the correct type }; } = JSON.parse(JSON.stringify(target)) as JSONCompatible<typeof target>; }
{ const target = (_number: number, _string: string) => true;
const typeCheck: never = JSON.parse( JSON.stringify(target) ) as JSONCompatible<typeof target>; }
{ const target = undefined;
const typeCheck: never = JSON.parse( JSON.stringify(target) ) as JSONCompatible<typeof target>; }
{ const target = Symbol();
const typeCheck: never = JSON.parse( JSON.stringify(target) ) as JSONCompatible<typeof target>; }}
こういう複雑な型定義は実用したくない。
遊び程度に使ったり、オープンソースとして公開したりするのがちょうどいい。