如何在TypeScript中从给定对象数组动态推断/映射类型?
TypeScript从对象数组动态生成指定结构的映射类型
给定如下对象数组:
const options = [ { name: "diameter", type: "number", value: 10, min: 0, max: 10 }, { name: "height", type: "number", value: 2, min: 0, max: 8 }, { name: "thickness", type: "number", value: 2, min: 0, max: 8 }, { name: "text", type: "string", value: "Hello World" }, ];
需要生成如下结构的类型:
type OptionsType = { diameter: { value: number; min: number; max: number }; height: { value: number; min: number; max: number }; thickness: { value: number; min: number; max: number }; text: { value: string }; };
原方案的问题
之前尝试的方案仅能生成number类型的结构,核心原因有两点:
- 原数组未添加类型断言,TypeScript自动将数组类型拓宽为包含所有属性的联合数组,丢失了每个元素的精确字面量类型信息(比如
name的具体字符串值、type的字面量类型)。 - 映射类型中的
Extract<T[number], { name: K }>逻辑错误,K是数组元素的类型而非name的类型,导致无法正确筛选对应元素的类型。
解决方案
步骤1:为数组添加as const断言
通过as const让TypeScript保留数组每个元素的精确字面量类型,避免类型自动拓宽:
const options = [ { name: "diameter", type: "number", value: 10, min: 0, max: 10 }, { name: "height", type: "number", value: 2, min: 0, max: 8 }, { name: "thickness", type: "number", value: 2, min: 0, max: 8 }, { name: "text", type: "string", value: "Hello World" }, ] as const;
步骤2:修正类型转换工具ParamType
直接基于元素的完整类型进行条件判断,确保能正确匹配type的字面量值:
type ParamType<T> = T extends { type: "number" } ? { value: number; min: number; max: number } : T extends { type: "string" } ? { value: string } : never;
如果需要保留原数组中value的精确字面量类型(比如10而非number),可以优化为:
type ParamType<T> = T extends { type: "number"; value: infer V; min: infer Min; max: infer Max } ? { value: V; min: Min; max: Max } : T extends { type: "string"; value: infer V } ? { value: V } : never;
步骤3:修正映射类型OptionsParams
遍历元组的每个元素类型,将元素的name作为键,对应值为ParamType处理后的类型:
type OptionsParams<T extends readonly { name: string }[]> = { [K in T[number] as K["name"]]: ParamType<K>; };
步骤4:生成目标类型
type OptionsType = OptionsParams<typeof options>;
最终生成的OptionsType完全符合预期结构:
type OptionsType = { diameter: { value: number; min: number; max: number; }; height: { value: number; min: number; max: number; }; thickness: { value: number; min: number; max: number; }; text: { value: string; }; };
内容的提问来源于stack exchange,提问作者Taner
相关产品推荐
相关产品推荐

