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

TypeScript中实现返回类型依赖另一泛型参数的HTTP请求函数

正确实现支持多解析类型的泛型HTTP请求函数

你的核心需求是让HTTP函数根据传入的解析类型(text/json/blob),精确返回对应类型的parsedBody,而不是模糊的联合类型。之前的写法问题在于没有建立解析类型和返回类型的精确映射,导致TypeScript无法推断出正确的类型关联。

下面是完整的正确实现方案:

第一步:定义解析类型与返回类型的映射

首先我们需要一个类型映射表,把每种解析方式和对应的返回类型绑定起来:

// 定义解析类型到返回类型的映射
type BodyParserMap = {
  text: string;
  json: unknown;
  blob: Blob;
};

// 提取所有支持的解析类型作为联合类型
type BodyParserType = keyof BodyParserMap;

// 自定义的HttpResponse类型(假设你之前的定义类似这样)
type HttpResponse<T> = Response & {
  parsedBody?: T;
  parsedSuccessfully: boolean;
};

第二步:实现泛型HTTP函数

我们使用两个泛型参数:

  • P:指定解析类型(text/json/blob),默认值为json
  • T:仅当P为json时生效,用于指定JSON解析后的类型,默认值为unknown

函数会根据P的类型,自动推断parsedBody的类型:

export async function http<
  P extends BodyParserType = 'json',
  T = P extends 'json' ? unknown : never
>(
  request: RequestInfo,
  init?: RequestInit,
  parserType: P = 'json' as P
): Promise<HttpResponse<P extends 'json' ? T : BodyParserMap[P]>> {
  const response = await fetch(request, init);
  const httpResponse: HttpResponse<any> = {
    ...response,
    parsedSuccessfully: false,
  };

  try {
    switch (parserType) {
      case 'text':
        httpResponse.parsedBody = await response.text();
        break;
      case 'blob':
        httpResponse.parsedBody = await response.blob();
        break;
      case 'json':
        httpResponse.parsedBody = await response.json() as T;
        break;
    }
    httpResponse.parsedSuccessfully = true;
  } catch (ex) {
    httpResponse.parsedSuccessfully = false;
  }

  return httpResponse as HttpResponse<P extends 'json' ? T : BodyParserMap[P]>;
}

第三步:调用示例

现在你可以根据不同的需求调用函数,TypeScript会自动推断出正确的parsedBody类型:

1. 解析为文本(string类型)

http<'text'>('http://localhost/text-endpoint').then((data) => {
  if (data.ok && data.parsedSuccessfully) {
    // data.parsedBody 类型为 string
    console.log(data.parsedBody.toUpperCase());
  }
});

2. 解析为JSON(指定自定义类型)

interface User {
  id: number;
  name: string;
}

http<'json', User>('http://localhost/user').then((data) => {
  if (data.ok && data.parsedSuccessfully) {
    // data.parsedBody 类型为 User
    console.log(data.parsedBody.name);
  }
});

// 或者省略第一个泛型参数,默认使用json解析
http<User>('http://localhost/user').then((data) => {
  // 效果同上,data.parsedBody 类型为 User
});

3. 解析为Blob

http<'blob'>('http://localhost/image').then((data) => {
  if (data.ok && data.parsedSuccessfully) {
    // data.parsedBody 类型为 Blob
    const imageUrl = URL.createObjectURL(data.parsedBody);
  }
});

为什么之前的写法有问题?

  • 第一种写法:直接把response.text()的结果赋值给parsedBody(类型为T),但string不一定兼容T,所以TypeScript报错。
  • 第二种写法:泛型约束V extends string = 'text' | 'json' | 'blob'没有建立V和返回类型的关联,TypeScript仍然无法推断parsedBody的具体类型。
  • 第三种写法:返回HttpResponse<T | string | Blob>是联合类型,无法精确区分每种解析方式对应的类型,不符合你需要的类型精确性要求。

内容的提问来源于stack exchange,提问作者Ivan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 21:42:41