如何为函数返回值实现TypeScript泛型映射类型?
解决TypeScript泛型函数调用对象方法后类型推导失效的问题
当你用泛型函数访问全局对象的属性并调用其返回的函数时,会遇到返回值类型推导失效的问题——明明定义了返回类型是对应Key的函数返回值,但TS却把它推断成所有可能返回值的联合类型,导致类型报错。
问题复现
const dict = { "foo": () => 1, "bar": () => "1" } as const; function getData<Key extends keyof typeof dict>(foobar: Key): ReturnType<typeof dict[Key]> { return dict[foobar](); // 报错:Type 'string | number' is not assignable to type 'ReturnType<{ readonly foo: () => number; readonly bar: () => string; }[Key]>' }
而如果对象属性直接是值而非函数,类型推导就能正常工作:
const dict = { "foo": 1, "bar": "1" } as const; function getData<Key extends keyof typeof dict>(foobar: Key): typeof dict[Key] { return dict[foobar]; // 正常推导,无报错 }
问题原因
TypeScript在这里无法自动把dict[foobar]()的返回值和ReturnType<typeof dict[Key]>做精确关联。dict[foobar]会被视为联合类型的函数((() => number) | (() => string)),调用后自然返回联合类型string | number,而泛型参数Key的具体类型没有被用来收窄这个联合类型的范围。
解决方案
方案1:类型断言(简单直接)
直接用类型断言告诉TS返回值的精确类型,这是最快的解决方式:
const dict = { "foo": () => 1, "bar": () => "1" } as const; function getData<Key extends keyof typeof dict>(foobar: Key): ReturnType<typeof dict[Key]> { return dict[foobar]() as ReturnType<typeof dict[Key]>; }
方案2:预定义映射类型(更清晰)
预先定义一个映射类型,明确每个Key对应的返回值类型,让TS能更清晰地推导:
const dict = { "foo": () => 1, "bar": () => "1" } as const; type Dict = typeof dict; // 定义每个Key对应的函数返回类型 type DictReturnTypes = { [K in keyof Dict]: ReturnType<Dict[K]> }; function getData<K extends keyof Dict>(foobar: K): DictReturnTypes[K] { return dict[foobar]() as DictReturnTypes[K]; }
方案3:函数重载(适合键较少的场景)
如果对象的键数量不多,可以用函数重载来明确每个参数对应的返回类型,这种方式不需要断言,类型最精确:
const dict = { "foo": () => 1, "bar": () => "1" } as const; // 重载签名 function getData(foobar: 'foo'): number; function getData(foobar: 'bar'): string; // 实现签名 function getData(foobar: keyof typeof dict) { return dict[foobar](); }
验证效果
现在调用getData('foo')会自动推导返回类型为number,调用getData('bar')推导为string,完全符合预期。
内容的提问来源于stack exchange,提问作者Krzysztof Krzeszewski
相关产品推荐
相关产品推荐

