如何获取索引访问器属性名?为personZoo配置正确类型
TypeScript动态添加属性的类型推导解决方案
以下是修改后的完整代码,能实现你期望的类型推导效果:
// 泛型IPerson接口,保留name和behavior的具体字面量类型 interface IPerson<Name extends string = string, Behavior extends Record<string, () => string> = Record<string, () => string>> { name: Name; behavior: Behavior; } const personZoo = { zooName: "person zoo", }; type PersonZoo = typeof personZoo; // 生成合并后的类型:将数组元素的name作为属性名,对应behavior作为属性类型 type MergeReturnType<T, K extends IPerson[]> = T & { [P in K[number]['name']]: K[number] extends infer Item ? Item extends IPerson<P, infer B> ? B : never : never; }; // 调整merge函数泛型,捕获数组元素的具体类型 function merge<T extends IPerson[]>( obj1: PersonZoo, obj2: readonly [...T], ): MergeReturnType<PersonZoo, T> { obj2.forEach((p) => { Object.defineProperty(obj1, p.name, { get() { return new Proxy(p.behavior, { get(target, key) { return target[key]; }, }); }, }); }); return obj1 as MergeReturnType<PersonZoo, T>; } // 去掉宽泛的IPerson[]标注,用as const保留字面量类型 const personArr = [ { name: "n1", behavior: { loudly: () => "speak loudly", }, }, { name: "n2", behavior: { singing: () => "singing", }, }, ] as const; const xx = merge(personZoo, personArr); // 类型推导正常,支持以下调用 xx.n1.loudly(); xx.n2.singing();
问题1:如何获取索引访问器的属性名?
要提取数组元素中name的具体字面量类型,核心是避免丢失类型细节:
- 不要给
personArr标注宽泛的IPerson[]类型——这个类型会让TypeScript把"n1"、"n2"当成普通string,丢失字面量信息; - 给数组添加
as const断言——强制TypeScript将数组元素的属性值视为不可变的字面量类型,这样就能精准捕获name的具体值。
问题2:如何让personZoo拥有动态添加属性的类型?
通过TypeScript的泛型+映射类型组合实现:
- 泛型捕获具体元素类型:
merge函数的泛型T会自动捕获personArr中每个元素的完整类型(包括name的字面量和behavior里的具体方法); - 映射类型生成动态属性:
MergeReturnType中的[P in K[number]['name']]会遍历数组中所有name值,将其作为新的属性名; - 条件类型提取behavior类型:通过嵌套条件类型,从每个元素中提取对应的
behavior类型,作为该属性的类型,这样xx.n1会被推导为{ loudly: () => string },xx.n2推导为{ singing: () => string }。
将IPerson改为泛型接口,是为了让它能适配不同的name字面量和behavior结构,而非固定成宽泛的索引类型。
内容的提问来源于stack exchange,提问作者NoobMaster
相关产品推荐
相关产品推荐

