如何实现TypeScript返回函数的多参数支持与自动类型推导?
解决TypeScript中函数返回值的类型推断问题
问题描述
我需要实现一个outer函数,它接收string类型参数,返回一个使用该参数的函数。返回的函数支持两种参数类型:
- 接收
string时,返回{ arg: string }类型的对象 - 接收
string[]时,返回该对象组成的数组
希望TypeScript能自动识别返回函数的参数与对应返回类型,无需每次使用时断言。
我尝试的代码如下:
function outer<T extends { arg: string }>(arg: string): (id: string) => T; function outer<T extends { arg: string }>(arg: string): (id: string[]) => T[]; function outer<T extends { arg: string }, S extends string | string[]>(arg: string): S extends string ? (id: string) => T : (id: string[]) => T[]; function outer<T extends { arg: string }>(arg: string): (id: string | string[]) => T | T[] { return (id: string | string[]) => { if (Array.isArray(id)) { return [{ arg }] as T[]; } return { arg } as T; } } type ArgType = { arg: string }; const result = outer<ArgType>('arg')(['id']);
执行时遇到错误:
Argument of type 'string[]' is not assignable to parameter of type 'string'.
问题原因
当前的重载写法存在逻辑问题:第三个重载使用了泛型S,但调用outer时并没有显式指定S的类型,TypeScript无法自动推断S,因此会优先匹配第一个重载(接收string参数的版本),导致传入数组时报错。
解决方案
我们可以通过两种方式修复这个问题,让TypeScript能正确推断返回函数的参数与返回类型:
方式一:为返回的函数定义重载
function outer<T extends { arg: string }>(arg: string): { (id: string): T; (id: string[]): T[]; } { return ((id: string | string[]) => { if (Array.isArray(id)) { return [{ arg }] as T[]; } return { arg } as T; }) as any; } type ArgType = { arg: string }; // 自动推断result为ArgType[] const result = outer<ArgType>('arg')(['id']); // 自动推断singleResult为ArgType const singleResult = outer<ArgType>('arg')('id');
方式二:使用泛型条件类型
function outer<T extends { arg: string }>(arg: string) { return <S extends string | string[]>(id: S): S extends string ? T : T[] => { if (Array.isArray(id)) { return [{ arg }] as T[]; } return { arg } as T; } } type ArgType = { arg: string }; // 自动推断result为ArgType[] const result = outer<ArgType>('arg')(['id']); // 自动推断singleResult为ArgType const singleResult = outer<ArgType>('arg')('id');
说明
- 方式一通过为返回的函数直接定义重载,明确告知TypeScript该函数支持的参数类型和对应返回值类型,TypeScript能准确匹配重载并完成类型推断。
- 方式二将泛型
S转移到返回的函数上,调用返回函数时,TypeScript会根据传入的id类型自动推断S,再通过条件类型确定最终返回值类型。
内容的提问来源于stack exchange,提问作者john
相关产品推荐
相关产品推荐

