Typescript 是否可以拆分泛型类型?

Typescript 是否可以拆分泛型类型?,typescript,Typescript,我调用的API端点以以下形状传递数据: interface ArrayResponse<T> { data: Partial<T>[] } 接口阵列响应{ 数据:部分[] } 接口对象响应{ 数据:部分 } 我编写了一个调用端点并返回响应的函数: function fetchOne<T>(path: string): ObjectResponse<T> { // HTTP call } function fetchMany<

我调用的API端点以以下形状传递数据:

interface ArrayResponse<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
}
函数fetchOne(路径:string):ObjectResponse{
//HTTP呼叫
}
函数fetchMany(路径:string):ArrayResponse{
//与上述HTTP逻辑完全相同
}
理想情况下,我希望有一个单一的响应类型:

interface Response<T> {
  data: Partial<T> | Partial<T>[]
}
接口响应{
数据:部分|部分[]
}
但是我真正想要的是能够让上面的类型根据传递给T的值来确定Partial或Partial[]

因此,
Response
将导致
data:Partial
Response[]
导致
data:Partial[]


我也乐于接受其他创造性的方法来达到同样的效果。

也许其他人可以比我更善于跳出框框思考,但你所要求的似乎不可能。发布此答案以供讨论。让我们尝试一个更简单的例子。您现在拥有的:

function returnString(): string {
  // returns string
}

function returnNumber(): number {
  // returns number
}

let string: string = returnString();
let number: number = returnNumber();
现在假设您尝试添加一个联合类型。请注意,无论函数在运行时发生什么,编译器都无法推断适当的类型:

type StringOrNumber = string | number;

function returnString(): StringOrNumber {
  // returns string
}

function returnNumber(): StringOrNumber {
  // returns number
}

let string: string = returnString();
// Type 'StringOrNumber' is not assignable to type 'string'.
//  Type 'number' is not assignable to type 'string'.

let number: number = returnNumber();
// Type 'StringOrNumber' is not assignable to type 'number'.
//  Type 'string' is not assignable to type 'number'.
你可以使用

因此,如果您有这些接口:

接口对象响应{
数据:部分
}
接口阵列响应{
数据:部分[]
}
类型IResponse=ObjectResponse | ArrayResponse;
您可以编写此保护函数:

函数isMultiResponse(响应:IResponse):响应为ArrayResponse{
返回Array.isArray(response.data);
}
此函数将获取一种类型的
i响应
,并确定它是否为
阵列响应
。这将允许您在运行时检查它是什么类型的

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

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

异步函数fetchData(路径:string):Promise{ //HTTP呼叫 返回get(路径); } 当您使用用户定义的guard函数时,typescript将知道响应的类型:

异步函数doSomething():Promise{ 常量路径=“”; const res:IResponse=等待获取数据(路径); 如果(isMultiResponse(res)){ //typescript将知道响应是ArrayResponse console.log(res.data[0]); }否则{ //typescript将知道响应是ObjectResponse console.log(res.data); } }
VS代码在运行这是可以做到的。但无论如何设置,调用时都需要手动设置泛型
T
,如
fetch(path)
,以获得有意义的响应类型,因为泛型
T
不可能从字符串路径推断出来

试试这个:

function fetchEither<T>(path: string): T extends (infer U)[] ? ArrayResponse<U> : ObjectResponse<T> {   
}


考虑到
T
无法推断,只返回联合类型并在运行时确定@thatkoogay建议的值可能更有意义。

在您的问题中,您编写了
Response[]
,但是
fetchMany
返回一个响应。
fetchMany
返回一个
ArrayResponse
,其中包括一个
数据:部分[]
签出我的答案!它也应该解决你的例子!调查<代码>函数returnStringOrNumber():T将执行此操作,但需要注意的是,如果没有可以推断的参数,则需要手动设置
T
<代码>函数returnStringOrNumber(输入:T):T其中输入类型与输出类型匹配最简单。哇!这些答案中出现了非常不同但非常酷的方法!喜欢这个讨论!:-)这太棒了。实际上,我不需要它来推断类型,因为我总是知道调用时返回的是什么(这是针对API端点的)。这与我的代码已经做的非常类似。问题是,在我已经知道响应将返回数组的情况下,我试图避免执行类型检查。如果您已经知道响应是数组,并且所使用的函数将永远不会获得其他值,只需使用
ArrayResponse
。如果有时获取一个数组,有时不获取,请将多响应与类型检查一起使用。这是您在不考虑Typescript类型的情况下所做的。我喜欢的另一种方法是将所有响应转换为相同的格式。所以,无论您得到什么,都要确保它是一个包装响应的数组(单个响应是长度为1的数组,而数组响应是长度大于等于1的数组)。这样,转换后就不需要任何类型检查。
function fetchEither<T>(path: string): T extends (infer U)[] ? ArrayResponse<U> : ObjectResponse<T> {   
}
const array = fetchEither<MyType[]>(""); // has type ArrayResponse<MyType>
const object = fetchEither<MyType>(""); // has type ObjectResponse<MyType>