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.ts
type Test = JSONCompatible<{
date: Date;
string: string;
}>;
制限事項
InfinityやNaNを正しく判定できません。
仕様では、InfinityやNaNをJSON.stringify()するとnullになる。
しかし、
JSONCompatible<typeof Infinity | typeof NaN>
はnumberとして判定される。 TypeScriptではInfinityやNaNを判定できない。
JavaScriptで動的に判定するしかない。
ソースコード
JSONCompatible.ts
export 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>;
}
}
こういう複雑な型定義は実用したくない。
遊び程度に使ったり、オープンソースとして公開したりするのがちょうどいい。