如何实现类Pick泛型的取值函数?解决TypeScript类型报错
问题分析与解决方案
为什么会报错?
- 动态键的类型推断限制:当你用动态变量
key作为对象字面量的键时,TypeScript会保守地将该对象的索引推断为string类型(JavaScript中对象键最终都会转为字符串或Symbol,TS对动态键的字面量做了宽泛处理),但你的返回类型是精确的Pick<T, K>(或等价映射类型),两者类型不匹配,因此触发报错。 - 返回类型的约束冲突:你定义的返回类型
Pick<T, K>要求必须包含K对应的属性,但当key不存在于obj时,你返回了空对象{},这并不满足Pick<T, K>的类型约束,这也是类型不兼容的核心原因之一。
无需类型断言的实现方式
方案1:调整返回类型为可选属性映射
将返回类型改为包含可选属性的映射类型,让空对象{}和包含目标键的对象都能匹配类型约束,TS会自动通过类型检查:
function pick<T extends {}, K extends keyof T>(obj: T, key: K): { [P in K]?: T[P] } { return key in obj ? { [key]: obj[key] } : {}; }
这个方案完全不需要类型断言,同时符合你“存在则返回对应属性对象,不存在则返回空对象”的需求。
方案2:利用Object.fromEntries的类型推断
如果你希望返回类型严格区分Pick<T, K>和{},可以使用Object.fromEntries来创建对象,它能更精准地推断键的类型:
function pick<T extends {}, K extends keyof T>(obj: T, key: K): Pick<T, K> | {} { if (!(key in obj)) { return {}; } return Object.fromEntries([[key, obj[key]]] as const) as Pick<T, K>; }
这里的as const是为了让TS将数组元素推断为字面量类型,从而让fromEntries能正确生成Pick<T, K>类型的对象,仅在最后一步需要一次轻量的类型断言(相比直接断言整个对象更安全)。
内容的提问来源于stack exchange,提问作者Levi Haskell
相关产品推荐
相关产品推荐

