如何在TypeScript中使用泛型保证访问成员的类型?
问题描述
我编写了一段代码,无泛型版本可以正常运行,但将类型替换为泛型T后出现类型错误:
declare function takesNumber(arg0: number): boolean; interface Thing { id: string; price: number; other: { stuff: boolean }; } type KeysMatching<T, V> = {[K in keyof T]-?: T[K] extends V ? K : never}[keyof T]; // 无泛型版本:运行正常 const noGeneric = function( vector: Thing, selectedAttributeName: KeysMatching<Thing, number>, ) { takesNumber(vector[selectedAttributeName]); } // 泛型版本:类型错误 const withGeneric = function<T>( vector: T, selectedAttributeName: KeysMatching<T, number>, ) { // 错误:类型“T[KeysMatching<T, number>]”无法赋值给类型“number”的参数。 takesNumber(vector[selectedAttributeName]); }
需要在保留泛型灵活性的前提下,保证访问成员的类型符合预期。
解决方案
核心问题是TypeScript无法直接推断T[KeysMatching<T, number>]必然是number类型。以下是几种可行的解决方式:
方案1:拆分泛型参数(推荐)
引入额外的泛型参数K,明确约束K是T中值类型为number的键,让类型关系更清晰:
declare function takesNumber(arg0: number): boolean; interface Thing { id: string; price: number; other: { stuff: boolean }; } const withGeneric = function<T, K extends keyof T>( vector: T, selectedAttributeName: K extends (T[K] extends number ? K : never) ? K : never ) { takesNumber(vector[selectedAttributeName]); // 类型推断正常,无报错 } // 验证:仅允许传入值为number的键 withGeneric({id: "1", price: 100}, "price"); // 合法 withGeneric({id: "1", price: 100}, "id"); // 报错,符合预期
方案2:优化KeysMatching类型定义
通过as关键字重构KeysMatching的映射逻辑,帮助TypeScript更准确地识别值类型:
declare function takesNumber(arg0: number): boolean; interface Thing { id: string; price: number; other: { stuff: boolean }; } type KeysMatching<T, V> = keyof { [K in keyof T as T[K] extends V ? K : never]: unknown }; const withGeneric = function<T>( vector: T, selectedAttributeName: KeysMatching<T, number> ) { takesNumber(vector[selectedAttributeName] as T[Extract<keyof T, KeysMatching<T, number>>]); }
方案3:类型断言(简化处理)
如果对类型严格性要求不高,可直接使用类型断言快速解决,但这种方式会弱化类型检查:
const withGeneric = function<T>( vector: T, selectedAttributeName: KeysMatching<T, number>, ) { takesNumber(vector[selectedAttributeName] as number); }
内容的提问来源于stack exchange,提问作者WBT
相关产品推荐
相关产品推荐

