如何在TypeScript中将JSON对象转换为类型,将字符串转换为类型?

huangapple go评论116阅读模式
英文:

How to convert a JSON object to a type, converting strings to types, in TypeScript?

问题

如何将base对象通过typeof base转换为GoalType,就像我尝试使用type FoundType = InterpolateType<typeof base>那样?

英文:

I have this TS playground:

const base = {
  user: {
    id: { type: 'string' },
    name: { type: 'string' }
  },
  post: {
    id: { type: 'string' },
    title: { type: 'string' },
    published: { type: 'boolean', defaultsTo: false },
    likes: { type: 'number', defaultsTo: 0 }
  }
}

type GoalType = {
  user: {
    id: string
    name: string
  }
  post: {
    id: string
    title: string
    published: boolean
  }
}

type InterpolateType<T> = {
  [K1 in T]: {
    [K2 in T[K1]]: T[K1]['type'] extends 'string' ? string : boolean
  }
}

type FoundType = InterpolateType<typeof base>

How can I convert the base object through typeof base to the GoalType, as I tried to do with type FoundType = InterpolateType<typeof base>?

答案1

得分: 1

我们首先需要使用const assertion来防止编译器扩展类型:

const base = {
  user: {
    id: { type: "string" },
    name: { type: "string" },
  },
  post: {
    id: { type: "string" },
    title: { type: "string" },
    published: { type: "boolean", defaultsTo: false },
    likes: { type: "number", defaultsTo: 0 },
    likesArr: { list: "true", type: ["string", "boolean"] },
  },
} as const;

我们还需要一个字符串到类型映射类型:

type StringToType = {
  string: string;
  boolean: boolean;
  number: number;
};

为了插值,我们将遍历类型并检查其属性的“type”是否在“StringToType”中。如果在那里,我们只返回“StringToType[type]”,否则,我们检查属性是否是具有{list: "true", type: readonly unknown[]}的列表。如果条件为真,我们通过使用“number”作为索引将“type”的元素传递给“StringToType”以检索“StringToType”中的所有元素:“StringToType[T[K][K2]["type"][number] & keyof StringToType]”。我们需要与“keyof StringToType”相交,以确保我们只传递存在于“StringToType”中的值。最后,我们通过在末尾添加“[]”来创建该数组。如果以上条件都不成立,我们只是返回“type”本身而没有任何修改。

type InterpolateType<T> = {
  [K in keyof T]: {
    [K2 in keyof T[K]]: "type" extends keyof T[K][K2]
      ? T[K][K2]["type"] extends keyof StringToType
        ? StringToType[T[K][K2]["type"]]
        : T[K][K2] extends { list: "true"; type: readonly unknown[] }
        ? StringToType[T[K][K2]["type"][number] & keyof StringToType][]
        : T[K][K2]["type"]
      : T[K][K2];
  };
};

用法:

// type Result = {
//     readonly user: {
//         readonly id: string;
//         readonly name: string;
//     };
//     readonly post: {
//         readonly id: string;
//         readonly title: string;
//         readonly published: boolean;
//         readonly likes: number;
//         readonly likesArr: (string | boolean)[];
//     };
// }
type Result = InterpolateType<typeof base>;

playground

英文:

We first need to use const assertion to prevent the compiler from widening the type:

const base = {
  user: {
    id: { type: &quot;string&quot; },
    name: { type: &quot;string&quot; },
  },
  post: {
    id: { type: &quot;string&quot; },
    title: { type: &quot;string&quot; },
    published: { type: &quot;boolean&quot;, defaultsTo: false },
    likes: { type: &quot;number&quot;, defaultsTo: 0 },
    likesArr: { list: &quot;true&quot;, type: [&quot;string&quot;, &quot;boolean&quot;] },
  },
} as const;

We will also need a string to type map type:

type StringToType = {
  string: string;
  boolean: boolean;
  number: number;
};

To interpolate, we will map through the type and check if the type of its property is in the StringToType. If it is there, we just return the StringToType[type], otherwise, we check whether the property is a list with {list: &quot;true&quot;, type: readonly unknown[]}. If the condition is true we pass the elements of type to StringToType by using number as the index to retrieve all the elements in the type: StringToType[T[K][K2][&quot;type&quot;][number] &amp; keyof StringToType]. We need to intersect with keyof StringToType to make sure that we only pass the values that exist in the StringToType. Finally, we make the array out of that by appending [] at the end. If none of the above conditions are true we just return the type itself without any modification.

type InterpolateType&lt;T&gt; = {
  [K in keyof T]: {
    [K2 in keyof T[K]]: &quot;type&quot; extends keyof T[K][K2]
      ? T[K][K2][&quot;type&quot;] extends keyof StringToType
        ? StringToType[T[K][K2][&quot;type&quot;]]
        : T[K][K2] extends { list: &quot;true&quot;; type: readonly unknown[] }
        ? StringToType[T[K][K2][&quot;type&quot;][number] &amp; keyof StringToType][]
        : T[K][K2][&quot;type&quot;]
      : T[K][K2];
  };
};

Usage:

// type Result = {
//     readonly user: {
//         readonly id: string;
//         readonly name: string;
//     };
//     readonly post: {
//         readonly id: string;
//         readonly title: string;
//         readonly published: boolean;
//         readonly likes: number;
//         readonly likesArr: (string | boolean)[];
//     };
// }
type Result = InterpolateType&lt;typeof base&gt;;

playground

huangapple
  • 本文由 发表于 2023年5月24日 18:40:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76322631.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定