Home > front end >  Implement JSON deserialization at the type level
Implement JSON deserialization at the type level

Time:11-26

I want to implement json serialization and deserialization at the type level of typescript.

I found an implementation of deserialization on github.

How can I implement serialization?

CodePudding user response:

I don't know what you mean by "type level serialization", but you could just wrap these in type checked functions. stringify will work fine on a type checked function since TypeScript can type-check the input:

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

Deserialization is more tricky, as the input string might contain anything. I don't think you can solve this compile-time. So, in this case if you need more guarantees you'll need to do run-time validation:

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

  // Do some validation

  return data;
}

If you're absolutelly sure that the input string complies with the interface then you can just cast:

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

CodePudding user response:

I implemented it myself:

// 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] }] }>

This is a rough implementation without recursive optimization. When there are too many json levels, errors may occur: Type instantiation is excessively deep and possibly infinite.

But this is enough for me, any optimization schemes please suggest in the comments.

Playground

  • Related