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

TypeScript无法推断外部泛型参数T的keyof T类型问题

TypeScript泛型函数类型推断异常问题

以下代码片段复现了问题:

type Bar = {
    prop: string,
    numProp: number
}

type Other = {
    prop: number,
    strProp: string
}

type Foo<T> = {
    func: <P extends keyof T>(propName: P, propValue: T[P]) => T;
}

const init = <T>(): Foo<T> => {
    return {func: <P extends keyof T>(propName: P, propValue: T[P]): T => { return {} as T }};
}

let foobar: Foo<Bar> = init<Bar>();

// 预期合法但报错:
const res1 = foobar.func('prop', 'hello');
// 错误:参数类型"prop"无法赋值给keyof T类型
// 错误:参数类型"hello"无法赋值给Bar[keyof T]类型

const res2: Bar = foobar.func('prop', 'hello');
// 合法

const res3 = foobar.func<keyof Bar>('prop', 'hello');
// 合法

const res4: Other = foobar.func('prop', 'hello');
// 错误:初始化类型Bar无法赋值给变量类型Other

const res5 = foobar.func<keyof Other>('prop', 'hello');
// 合法:不符合预期逻辑

const res6 = foobar.func<keyof Other>('prop', 5);
// 错误:参数类型5无法赋值给Bar[keyof Bar]类型

const res7 = foobar.func<keyof Other>('strProp', 'hello');
// 错误:参数类型"strProp"无法赋值给keyof Bar类型

const res8: Bar = foobar.func('prop', 5);
// 错误:参数类型5无法赋值给Bar["prop"]类型

const res9 = foobar.func<keyof Bar>('prop', 5);
// 合法

问题分析

调用init<Bar>()初始化foobar后,TypeScript无法在func内部正确将泛型T识别为Bar,导致keyof T没有展开为keyof Bar。

  • 显式指定返回值类型为Bar时,keyof T会被正确识别为keyof Bar,代码合法;从第二个参数的错误信息能看出,TypeScript其实已经知道T是Bar,问题仅出在<P extends keyof T>的类型推断上。
  • 显式指定P为keyof Bar时代码合法,但存在逻辑矛盾:res4中把返回值设为Other会报错(正确识别返回类型为Bar),但res5指定P为keyof Other却被允许;而res6和res7又会因实际遵循Bar的键规则报错,说明传入的keyof Other被忽略。
  • res8被正确识别为非法,但res9却被允许——尽管Bar['prop']是string而非number,说明TypeScript对keyof Bar的类型约束也未完全生效。

预期行为

希望T在func中被明确识别为Bar,P被约束为extends keyof Bar,仅允许prop和numProp作为propName的有效值;调用func('prop')时,第二个参数类型自动匹配Bar['prop']对应的string类型,即以下代码应合法:

let foobar: Foo<Bar> = init<Bar>();
const res1 = foobar.func('prop', 'hello');

内容的提问来源于stack exchange,提问作者iainmackay85

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 04:10:36