如何结合字符串字面量类型实现TypeScript函数重载并匹配返回类型?
如何让TypeScript函数根据联合类型参数返回对应类型?
我定义了字符串字面量联合类型:
export type FooMedia = "course" | "article" | "podcast";
接着写了重载的getFoo函数,根据传入的不同类型返回对应的数据结构:
function getFoo( id: string, type: "article" ): Promise<AxiosResponse<Article, any>>; function getFoo( id: string, type: "podcast" ): Promise<AxiosResponse<Podcast, any>>; function getFoo( id: string, type: "course" ): Promise<AxiosResponse<Course, any>>; function getFoo<T extends FooMedia, R extends Media>(id: string, type: T) { const endpoint = `/api/${type}/${id}`; return axios.get<R>(endpoint); }
但当用FooMedia类型的变量调用getFoo时,出现类型错误:FooMedia无法赋值给"article"/"podcast"/"course"类型参数:
function bar(type: FooMedia) { return getFoo("t", type); // 类型错误 }
想实现的效果是:根据传入的FooMedia类型参数,自动推导对应的返回类型,这是否可行?
问题原因
函数重载的每个签名仅匹配单个字面量类型,而FooMedia是三个字面量的联合类型。当传入联合类型变量时,TypeScript无法确定它具体对应哪个重载分支,因此抛出类型不匹配的错误。
可行解决方案:用泛型+类型映射替代重载
通过定义一个类型映射表,将每个FooMedia成员与对应的返回类型关联,再用泛型约束实现自动推导:
- 先定义类型映射关系:
// 假设Article、Podcast、Course是已定义的业务类型 type MediaResponseMap = { article: Promise<AxiosResponse<Article, any>>; podcast: Promise<AxiosResponse<Podcast, any>>; course: Promise<AxiosResponse<Course, any>>; };
- 改写
getFoo函数,利用泛型和映射类型推导返回值:
function getFoo<T extends FooMedia>(id: string, type: T): MediaResponseMap[T] { const endpoint = `/api/${type}/${id}`; // 这里需要用类型断言,因为axios无法自动推导具体返回类型 return axios.get(endpoint) as MediaResponseMap[T]; }
- 现在调用
bar函数时,TypeScript会自动推导返回类型为对应分支的联合:
function bar(type: FooMedia) { return getFoo("t", type); // 返回类型为:Promise<AxiosResponse<Article, any>> | Promise<AxiosResponse<Podcast, any>> | Promise<AxiosResponse<Course, any>> }
如果需要在调用时精确推导单个分支的类型,直接传入字面量即可:
const articleRes = getFoo("1", "article"); // 类型为Promise<AxiosResponse<Article, any>>
补充:保留重载的兼容写法
如果想保留原有重载,需要调整实现函数的签名,让它能兼容联合类型参数:
// 重载签名不变 function getFoo( id: string, type: "article" ): Promise<AxiosResponse<Article, any>>; function getFoo( id: string, type: "podcast" ): Promise<AxiosResponse<Podcast, any>>; function getFoo( id: string, type: "course" ): Promise<AxiosResponse<Course, any>>; // 实现签名改为接受FooMedia联合类型,返回对应联合类型 function getFoo(id: string, type: FooMedia): Promise<AxiosResponse<Article | Podcast | Course, any>> { const endpoint = `/api/${type}/${id}`; return axios.get(endpoint); }
不过这种写法的类型推导精度不如映射类型,返回值会是所有可能类型的联合,无法在调用时精确到单个分支。
内容的提问来源于stack exchange,提问作者LoyalPotato
相关产品推荐
相关产品推荐

