TypeScript中调用签名与泛型约束的keyof行为问题
这不是TypeScript的bug,是你对泛型约束的设置有误,导致类型推断不符合预期。
问题根源
你修改后的GetV接口中,内部函数的泛型参数K的约束是keyof KV(接口层级的默认泛型KV,即{[K: string]: any}),而非keyof SUBKV。当你显式指定SUBKV为SpecificTypeInformation时,K的约束仍然是宽泛的string,TypeScript会将K推断为string,因此SUBKV[K]会被解析为SpecificTypeInformation[string],也就是所有值的联合类型'bar'|'baz'。
解决方案
有两种更简洁的写法可以满足需求,且无需导出GetV接口:
方案1:调整泛型约束的指向
修改MyLib.d.ts中GetV接口的内部函数泛型约束,让K继承自SUBKV的键而非KV:
// MyLib.d.ts interface GetV<KV = {[K: string]: any}> { <SUBKV extends KV = KV, K extends keyof SUBKV = keyof SUBKV>(options:{ field: K }): SUBKV[K]; } export function getV: GetV;
此时调用getV<SpecificTypeInformation>({ field: 'foo' }),K会被正确推断为'foo',返回类型为'bar'。
方案2:直接定义泛型函数类型
放弃泛型接口,直接将getV定义为泛型函数,更直观且避免嵌套泛型的混淆:
// MyLib.d.ts export function getV<KV extends object = { [K: string]: any }, K extends keyof KV>(options: { field: K }): KV[K];
调用方式不变,TypeScript会根据你指定的KV类型和传入的field参数,自动推断出精确的返回类型:
type SpecificTypeInformation = { foo: 'bar'; goo: 'baz' }; const x: 'bar' = getV<SpecificTypeInformation>({ field: 'foo' }); // 类型正确,无报错
为什么原类型断言写法有效
你用as MyLib.GetV<SpecificTypeInformation>断言时,实际上是将getV的类型从GetV<{[string]:any}>转换为GetV<SpecificTypeInformation>,此时内部函数的K约束变为keyof SpecificTypeInformation,传入'foo'自然会推断出正确的返回类型。但这种写法确实繁琐,上述两种方案可以替代它。
内容的提问来源于stack exchange,提问作者gillyspy

