如何用TypeScript为数组生成的对象定义返回类型?
为数组转对象的函数定义TypeScript返回类型
需求描述
需要实现一个TypeScript函数,接收一组包含id属性的对象数组(id的取值来自指定联合类型),返回以这些id为属性名的对象。要求TypeScript能准确推断返回对象的可用属性——即只有传入数组中出现过的id才会被识别为有效属性。
示例代码(原需求)
type Names = "input" | "button" | "form"; type FuncParam = { id: Names }; type FuncReturnType = ??????; /* * 将包含id的对象数组转换为:以id为属性名、值为"hello"的对象 */ const defineSomethings = (somethings: FuncParam[]) => { const obj = {} as FuncReturnType; // 初始化返回对象 somethings.map(({ id }) => obj[id] = "hello"); // 填充属性 return obj; }; const A = defineSomethings( { id: "input" }, { id: "form" } ); const B = defineSomethings( { id: "button" } ); // 期望的类型检查结果 A.input // ✅ 正常识别 A.form // ✅ 正常识别 A.button // ❌ 报错,A无此属性 B.input // ❌ 报错 B.form // ❌ 报错 B.button // ✅ 正常识别
解决方案
核心是用泛型捕获传入数组的具体id字面量类型,而非依赖固定联合类型,让TypeScript精准推断返回对象的属性。修改后的代码如下:
type Names = "input" | "button" | "form"; type FuncParam = { id: Names }; // 用泛型T捕获传入数组的具体元素类型 const defineSomethings = <T extends FuncParam[]>(somethings: [...T]) => { // 返回对象的类型:以数组中所有id为属性名,值为string类型 const obj = {} as Record<T[number]['id'], string>; // 遍历赋值(用forEach替代map更合理,因为不需要返回新数组) somethings.forEach(({ id }) => obj[id] = "hello"); return obj; }; // 调用时传入数组(修正原代码参数写法错误) const A = defineSomethings([ { id: "input" }, { id: "form" } ]); const B = defineSomethings([ { id: "button" } ]); // 类型检查符合预期 A.input; // ✅ A.form; // ✅ A.button; // ❌ 类型错误:类型“Record<"input" | "form", string>”上不存在属性“button” B.input; // ❌ 类型错误 B.form; // ❌ 类型错误 B.button; // ✅
关键细节说明
- 泛型约束与元组推断:
T extends FuncParam[]确保传入数组元素符合FuncParam结构,[...T]是元组语法,让TypeScript捕获数组中每个元素的具体id字面量类型(而非笼统推断为FuncParam[])。 - 返回类型推导:
T[number]['id']提取数组中所有元素的id,组成精准的字面量联合类型(比如A对应的是"input" | "form"),再通过Record<K, V>工具类型定义返回对象结构——属性名为该联合类型,属性值为string。 - 类型断言初始化:用
as Record<T[number]['id'], string>告知TypeScript初始化的空对象最终会被填充为指定结构,避免类型报错。
内容的提问来源于stack exchange,提问作者carpacciao
相关产品推荐
相关产品推荐

