使用Generator函数中的yield时TypeScript无法推导Promise类型的问题排查
我之前也碰到过一模一样的情况,这其实不是你tsconfig配置遗漏了什么,而是TypeScript对Generator函数里yield的返回值类型推导本身就存在局限。
问题根源
当你在Generator里yield一个Promise(比如axios.get<T>(url)返回的Promise<AxiosResponse<T>>)时,TypeScript默认不知道这个Promise会被外部的迭代器(比如co或者自定义的异步runner)解析成什么值。Generator的类型系统需要明确知道yield出去的值被处理后,回传给Generator内部的具体类型,如果没有显式注解,它就只能默认推导为any。
解决方案
这里有几种方式可以让TypeScript自动推导正确的类型,不用每次手动写AxiosResponse<T>:
1. 显式指定Generator的类型签名
给Generator函数定义完整的类型参数,明确yield出去的类型、Generator最终返回的类型,以及从外部传入Generator的类型(也就是Promise resolve后的值):
import axios, { AxiosResponse } from 'axios'; // 类型签名格式:Generator<YieldType, ReturnType, NextType> function* foo<T>(): Generator<Promise<AxiosResponse<T>>, void, AxiosResponse<T>> { const { data } = yield axios.get<T>(url); // 现在 data 会被正确推导为 T 类型 }
这里的NextType就是Promise被resolve后,yield表达式会接收到的类型,TypeScript就能据此自动推导出data的类型。
2. 使用Awaited工具类型简化注解
如果你不想写完整的Generator类型签名,可以用Awaited来提取Promise解析后的类型,代码会更简洁:
import axios from 'axios'; function* foo<T>() { const { data }: Awaited<ReturnType<typeof axios.get<T>>> = yield axios.get<T>(url); // data 同样会被正确推导为 T 类型 }
ReturnType<typeof axios.get<T>>会获取axios.get<T>的返回类型(也就是Promise<AxiosResponse<T>>),再通过Awaited提取出Promise resolve后的AxiosResponse<T>类型。
3. 结合Generator runner的类型定义
如果你使用像co这样的成熟Generator runner,它的类型定义已经处理了Promise的解析逻辑。只要安装了@types/co,就能借助它的类型自动推导:
import co from 'co'; import axios from 'axios'; function* foo<T>() { const { data } = yield axios.get<T>(url); // data 会被正确推导为 T 类型 } // co会自动处理Promise,并让TypeScript识别到yield的返回类型 co(foo<T>());
关于tsconfig的补充说明
你的tsconfig里noImplicitAny: false只是允许隐式any类型存在,但并不会让TypeScript自动推导Generator中yield的异步值类型。哪怕开启strict: true,这个问题依然存在——因为这是Generator类型系统的设计特性,不是配置能解决的。
内容的提问来源于stack exchange,提问作者Kevin Whitaker

