如何基于请求类型推断TypeScript函数的响应返回类型?
如何基于请求类型推断TypeScript函数的响应返回类型?
这个问题我之前也踩过坑,TypeScript在处理判别联合和泛型索引的时候,有时候会因为类型检查的严格性“认死理”,咱们一步步来把它搞定。
先看你原来的代码问题:你用T extends MyRequest作为泛型约束,虽然逻辑上T['kind']肯定是MyResponse的有效键,但TypeScript编译器没办法直接确认这层关联,所以才会抛出“不能用T['kind']索引MyResponse”的错误。
下面给你两种可行的解决思路,都是让编译器能清晰识别请求和响应的类型映射关系:
方案一:直接针对请求的kind约束泛型
这种方式最简洁,把泛型参数直接绑定到请求类型的kind枚举成员上,让编译器明确索引的有效性:
首先保留你原来的枚举和类型定义:
enum MyRequestKind { Foo = 'foo', Bar = 'bar', } type MyRequest = | { kind: MyRequestKind.Foo, fooParam: number } | { kind: MyRequestKind.Bar, barParam: string }; type MyResponse = { [MyRequestKind.Foo]: boolean; [MyRequestKind.Bar]: { result: string, error: string }; };
然后重写send函数,泛型约束为MyRequestKind的成员,并用Extract工具类型提取对应kind的请求类型:
function send<K extends MyRequestKind>(req: Extract<MyRequest, { kind: K }>): Promise<MyResponse[K]> { // 这里写实际的MessagePort发送逻辑,比如: // const port = getYourMessagePort(); // return new Promise((resolve) => { // port.postMessage(req); // port.onmessage = (e) => resolve(e.data); // }); // 下面是模拟响应的示例代码 return new Promise((resolve) => { if (req.kind === MyRequestKind.Foo) { resolve(true as MyResponse[K]); } else { resolve({ result: "success", error: "" } as MyResponse[K]); } }); }
调用的时候,TypeScript会自动推断正确的返回类型:
// 推断返回类型为Promise<boolean> const fooResp = send({ kind: MyRequestKind.Foo, fooParam: 123 }); // 推断返回类型为Promise<{ result: string, error: string }> const barResp = send({ kind: MyRequestKind.Bar, barParam: "hello world" });
方案二:用条件类型显式建立请求-响应映射
如果不想修改泛型的约束方式,也可以定义一个条件类型,把请求类型和响应类型的对应关系明确写出来:
// 定义一个工具类型,根据请求类型返回对应的响应类型 type ResponseForRequest<T extends MyRequest> = T extends { kind: MyRequestKind.Foo } ? MyResponse[MyRequestKind.Foo] : T extends { kind: MyRequestKind.Bar } ? MyResponse[MyRequestKind.Bar] : never; // 然后修改send函数的返回类型为这个工具类型 function send<T extends MyRequest>(req: T): Promise<ResponseForRequest<T>> { // 同样的发送逻辑 return new Promise(resolve => { if (req.kind === MyRequestKind.Foo) { resolve(true as ResponseForRequest<T>); } else { resolve({ result: "success", error: "" } as ResponseForRequest<T>); } }); }
这种方式也能正常工作,但如果后续要新增更多请求类型,就得手动给条件类型加分支,不如第一种方式扩展性好。
核心思路其实就是:让TypeScript编译器明确感知到“这个请求类型对应的响应类型是什么”,要么通过泛型直接绑定枚举成员,要么用条件类型把映射关系写死,这样它就不会再报错啦。
内容来源于stack exchange
相关产品推荐
相关产品推荐

