You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 17:21:07