You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于请求类型推断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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 12:12:59