在类型级别实现 JSON 反序列化

Posted

技术标签:

【中文标题】在类型级别实现 JSON 反序列化【英文标题】:Implement JSON deserialization at the type level 【发布时间】:2022-01-04 08:52:38 【问题描述】:

我想在typescript的类型级别实现json序列化和反序列化。

我在github 上找到了反序列化的实现。

如何实现序列化?

【问题讨论】:

也许您需要的是 class-validatorclass-transformer 包。 【参考方案1】:

我自己实现的:

// https://github.com/type-challenges/type-challenges/issues/2835
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer U) => any ? U : never
type LastUnion<T> = UnionToIntersection<T extends any ? (x: T) => any : never> extends (x: infer L) => any ? L : never
export type UnionToTuple<T, Last = LastUnion<T>> = [T] extends [never] ? [] : [...UnionToTuple<Exclude<T, Last>>, Last]

type obj2json<keys, T> = keys extends []
    ? ''
    : keys extends [infer a]
    ? a extends string
        ? a extends keyof T
            ? `"$a":$stringify<T[a]>`
            : never
        : never
    : keys extends [infer a, ...infer as]
    ? a extends string
        ? a extends keyof T
            ? `"$a":$stringify<T[a]>,$obj2json<as, T>`
            : never
        : never
    : never
type arr2json<items> = items extends []
    ? ''
    : items extends [infer a]
    ? `$stringify<a>`
    : items extends [infer a, ...infer as]
    ? `$stringify<a>,$arr2json<as>`
    : never
type stringify<T> = T extends object
    ? T extends Array<unknown>
        ? `[$arr2json<T>]`
        : UnionToTuple<keyof T> extends infer keys
        ? `$obj2json<keys, T>`
        : never
    : T extends string
    ? `"$T"`
    : T extends number
    ? `$T`
    : T extends boolean
    ? `$T`
    : never

type x1 = stringify< a: '1'; b: 2; c:  a: 1  >
type x2 = stringify< a: [1, 2, 3] >
type x3 = stringify< a: [1, 2,  a: 1; b: 2; c: [1, true] ] >

这是一个没有递归优化的粗略实现。当json级别过多时,可能会出现错误:Type instantiation is excessively deep and possibly infinite.

但这对我来说已经足够了,任何优化方案请在cmets中提出。

Playground

【讨论】:

哦,哇。这有什么用例? ? 本来是想构建一个新的类型系统,打算用json来表示,所以自然认为需要序列化和反序列化,但是至今没有用过序列化。也许这个想法是错误的......【参考方案2】:

我不知道您所说的“类型级序列化”是什么意思,但您可以将它们包装在类型检查函数中。 stringify 可以在类型检查函数上正常工作,因为 TypeScript 可以对输入进行类型检查:

function serialize(data: SomeInterface): string 
  return JSON.stringify(data);

反序列化更加棘手,因为输入 string 可能包含任何内容。我认为您无法解决此编译时问题。因此,在这种情况下,如果您需要更多保证,则需要进行运行时验证:

function deserialize(input: string): SomeInterface 
  const data = JSON.parse(input);

  // Do some validation

  return data;

如果您绝对确定输入字符串符合接口,那么您可以进行转换:

const data = JSON.parse(input) as SomeInterface;

【讨论】:

谢谢你的回答,但我想要的是类型级别的操作。我已经实施了。大家可以参考我的回答,注意x1x2x3的类型。 “类型级别”是指对类型进行编程,类似于函数调用,只是参数都是类型而不是值,调用的结果也是类型而不是值。通过一个或多个类型生成一个新类型。这是类型级别的编程。对我来说,这可以帮助我更好地描述程序的类型约束。

以上是关于在类型级别实现 JSON 反序列化的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot 动态/注解自定义 JSON 反序列化器

Json反序列化之ObjectMapper(自定义实现反序列化方法)

Json反序列化之ObjectMapper(自定义实现反序列化方法)

C# Json反序列化 数据协定类型 无法反序列化 由于未找到必需的数据成员

Python Json序列化与反序列化

Newtonsoft.Json 处理多态类型的反序列化