在类型级别实现JSON规范化

b5lpy0ml  于 2023-10-21  发布在  其他
关注(0)|答案(3)|浏览(174)

我想在typescript的类型级别实现json序列化和非序列化。
我在github上找到了一个实现。
如何实现序列化?

vmjh9lq9

vmjh9lq91#

我自己实现的:

// 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.
但这对我来说已经足够了,任何优化方案请在评论中提出建议。
操场

kqhtkvqz

kqhtkvqz2#

我不知道你说的“类型级序列化”是什么意思,但是你可以把这些封装在类型检查函数中。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;
ubby3x7f

ubby3x7f3#

不确定理解什么是“在类型级别”,但如果它意味着用类示例代替值(例如Date示例或您自己的类示例),那么您可能会对@badcafe/jsonizer感兴趣:https://badcafe.github.io/jsonizer/#/

相关问题