TypeScript中如何正确声明支持动态推导的getProperty函数
TypeScript
prop 函数返回值类型推导异常解决方案 问题描述
你需要实现一个通用的属性读取函数prop(propName, obj),希望返回值类型能根据传入的键和对象自动推导,且不需要强制校验传入的键必须是对象的合法键,但自行编写的泛型声明存在推导不一致的问题:
- 官方提供的基础实现
declare function getProperty<T, U extends keyof T>(name: U, obj: T): T[U];会强制校验键名合法性,不符合动态传参需求 - 自行实现的
PropOfObj工具类型单独测试时推导正常,但在函数泛型场景下推导异常:传入未断言的字面量键时返回unknown,只有对键做类型断言才能得到正确的属性类型
复现代码
type PropOfObj<T, U> = U extends keyof T ? T[U] : unknown; declare function prop<T, U>(propName: U, obj: T): PropOfObj<T, U>; // 推导异常的场景 const n1 = prop('name', { name: 'David' }); // 预期为string,实际推导为unknown const n2 = prop('name' as 'name', { name: 'David' }); // 断言后推导正常为string const n3 = prop('name', { [Symbol('name')]: 'David' }); // 非预期推导为string // 单独测试工具类型推导正常 const obj = { name: 'David' }; const key = 'name'; type T = typeof obj; // { name: string } type U = typeof key; // "name" type Extended = U extends keyof T ? T[U] : unknown; // 推导为string,符合预期
异常核心原因
问题出在函数泛型推导优先级和字符串字面量类型的推导规则上:
- 当直接传入字符串字面量
'name'作为propName参数时,TypeScript默认会把泛型U推导为string宽类型,而非字面量类型'name' - 此时传入的对象类型
T的键是'name',string无法匹配keyof T的字面量类型,因此条件类型U extends keyof T判断为false,返回unknown - 单独测试工具类型时,
key变量的类型被显式推导为字面量'name',因此条件类型可以正常匹配
调整后的泛型声明
只需要给泛型U增加键类型基础约束,告知TypeScript推导propName参数类型时优先保留字面量类型,不要放宽为宽类型:
type PropOfObj<T, U> = U extends keyof T ? T[U] : unknown; // 调整点:为U增加基础约束,要求是可作为对象键的类型范围 declare function prop<T, U extends string | number | symbol>(propName: U, obj: T): PropOfObj<T, U>;
调整后的推导效果
const n1 = prop('name', { name: 'David' }); // 推导为string,符合预期 const n2 = prop('age', { name: 'David' }); // 推导为unknown,符合预期(age不是对象的合法键) const n3 = prop(Symbol('name'), { [Symbol('name')]: 'David' }); // 推导为string,符合预期
该约束不会强制校验传入的键是否为对象的合法键,仅优化类型推导逻辑:
- 传入合法键时,返回对应属性的精确类型
- 传入非法键时,返回
unknown,完全满足动态传参的需求
内容的提问来源于stack exchange,提问作者Sheldon
相关产品推荐
相关产品推荐

