首页 > 解决方案 > 是否可以拆分泛型类型?

问题描述

我调用的 API 端点以以下形式提供数据:

interface ArrayResponse<T> {
   data: Partial<T>[]
}
interface ObjectResponse<T> {
   data: Partial<T>
}

我编写了一个调用端点并返回响应的函数:

function fetchOne<T>(path: string): ObjectResponse<T> {
  // HTTP call
}

function fetchMany<T>(path: string): ArrayResponse<T> {
  // Exact same HTTP logic as above
}

理想情况下,我希望有一个单一的响应类型:

interface Response<T> {
  data: Partial<T> | Partial<T>[]
}

但我真正想要的是能够让上述类型根据传递给 T 的值来确定 Partial 或 Partial[]。

所以Response<T>会导致data: Partial<T>Response<T>[]结果data: Partial<T>[]

我也对达到相同效果的其他创造性方法持开放态度。

标签: typescript

解决方案


您可以使用用户定义的类型防护来做到这一点

所以,如果你有这些接口:

interface ObjectResponse<T> {
  data: Partial<T>
}

interface ArrayResponse<T> {
  data: Partial<T>[]
}

type IResponse<T> = ObjectResponse<T> | ArrayResponse<T>;

您可以编写此保护函数:

function isMultiResponse<T>(response: IResponse<T>): response is ArrayResponse<T> {
  return Array.isArray(response.data);
}

此函数将获取 类型IResponse<T>,并确定它是否为ArrayResponse<T>. 这将允许您在运行时检查它是哪种类型。

这转换为常规 javascript,但 typescript 知道此函数确定类型。

因此,对于您的两个功能,如果逻辑相同,您可以将它们组合起来:

async function fetchData<T>(path: string): Promise<IResponse<T>> {
  // HTTP call
  return get(path);
}

并且当你使用用户定义的保护函数时,typescript 会知道响应是哪种类型:

async function doSomething<T>(): Promise<void> {
  const path = '';
  const res: IResponse<T> = await fetchData(path);

  if (isMultiResponse(res)) {
    // typescript will know the response is ArrayResponse<T>
    console.log(res.data[0]);
  } else {
    // typescript will know the response is ObjectResponse<T>
    console.log(res.data);
  }
}

VS 代码在行动


第一种Partial<T>[]如预期 vscode 将此检测为 T[]


第二种Partial<T>如预期 vscode 将此检测为 T

编译代码

当编译成 JavaScript 时,isMultiResponse函数和 if 语句显然会在那里。但是你可能想要在你的代码中的某个地方进行这样的检查 :-) 使用Type Guard编写它会在运行时进行必要的检查,但也会让 typescript 知道这两种不同的场景并相应地给你反馈


推荐阅读