我有一个远程服务,它返回包含对象或对象数组的JSON.有时,当数组中有一个对象时,服务返回一个对象而不是包含一个对象的array.有一个函数实现了对服务的调用:

export async function ApiCall<T>(funcName : string, args? : ArgList) : Promise<T> {
  var callPromise : Promise<T>;
  //...implementation
  return callPromise;
}
// usage:
ApiCall<MyType>("foo").then((myTypeObj) => {var x = myTypeObj.myField; /*...*/}); //1
ApiCall<MyType[]>("bar").then((array) => { for(var i = 0; i < array.length; ++i) /*...*/  }); //2

我想在这个函数中引入一个补丁,这样用法就保持不变--对"bar"的调用不必知道API调用的结果可能是一个MyType对象.然而,我不知道如何获得在Case 1和Case 2中执行不同任务的编译代码.

我认为编写两个不同版本的ApiCall并让编译器 Select 调用哪个版本是可行的,因为它在一个调用点具有所有相关信息,但似乎TypeScrip中的函数重载对此太有限了.

强制所有调用者传递一个额外的标志是可行的,但解决方案是丑陋的,如果需要更多的补丁,将变得更糟.

TypeScript中是否有类似于C++模板专门化的技巧和/或其他方法来解决所描述的问题?重申一下,我希望一个类似于函数的调用ApiCall来执行如下操作:

return callPromise.then((result) => {return Array.isArray(result) ? result : [result];});

if and only ifT是一个array.

推荐答案

不,这在打字脚本中是故意不可能的.Generics(您所称的"模板",这不是常规术语,主要是因为它暗示了您正在设想的那种编译时重写),以及静态类型系统的其余部分,在编译时完全是erased.真的被抹go 了.因此,必然地,表单apiCall<T>("foo")的打字脚本中的函数调用将被编译成表单apiCall("foo")的JavaScript.这意味着无论如何,apiCall<MyType>("foo")apiCall<MyType[]>("foo")都将被编译成相同的JavaScript,因此您的补丁将没有什么可操作的.


除了该语言早期的几个例外(enum是一个大的例外),TypeScrip并不意味着add对JavaScript有任何东西.为了让开发人员更容易地发现错误,它应该只包含describe个在JavaScript中发生的事情.因此,"基于类型系统的结果发出不同的代码"是显式的Non-Goal of TypeScript(参见#5).如果编译器必须查看apiCall<X>("foo")并计算X以确定它是否是数组(可能type X = string[]在代码中的某个位置?或者X可能是一个从constrainedany[]的泛型类型参数?)来确定生成的JavaScript是什么,那么它就违反了该规则.

因此,编写TypeScript代码就像编写JavaScript代码一样,除了有一个类型判断器可以与之通信,以帮助跟踪您正在做的事情. 当你想在运行时发生一些事情时,你需要编写JavaScript代码来完成它,所以你必须自己编写两个函数并根据需要调用它们:

declare function apiCallArray<T extends any[]>(x: string): Promise<T>
declare function apiCallNonArray<T>(x: string): Promise<T>

或者有一个函数,它接受一个额外的参数,以便JavaScript可以委托给正确的版本:

declare function apiCall<T extends any[]>(arrayVersion: true, x: string): Promise<T>
declare function apiCall<T>(arrayVersion: false, x: string): Promise<T>

或者任何其他方法,只要它在TypeScript代码的未擦除部分中显式编码.

Playground link to code

Typescript相关问答推荐

如何将绑定到实例的类方法复制到类型脚本中的普通对象?

如何推断类方法未知返回类型并在属性中使用它

如何创建泛型类型组件来使用React Native的FlatList或SectionList?'

如何在排版中正确键入中间件链和控制器链

如何键入函数以只接受映射到其他一些特定类型的参数类型?

如何编写一个类型脚本函数,将一个对象映射(转换)为另一个对象并推断返回类型?

`fetcher.load`是否等同于Get Submit?

在打印脚本中使用泛型扩展抽象类

如何通过TypeScrip中的函数防止映射类型中未能通过复杂推理的any值

如何在省略一个参数的情况下从函数类型中提取参数类型?

可赋值给类型的类型,从不赋值

在REACT查询中获取未定义的isLoading态

任何导航器都未处理有效负载为";params";:{";roomId";:";...";}}的导航操作

如何使用angular15取消选中位于其他组件中的复选框

内联类型断言的工作原理类似于TypeScrip中的断言函数?

断言同级为true或抛出错误的Typescript函数

Next 13 + React 18 createContext 类型的构建问题

基于参数的 TS 返回类型

基于泛型类型的条件类型,具有条目属性判断

为什么 typescript 在对象合并期间无法判断无效键?