这实际上是 existential generic types 的规范用例,TypeScript 不直接支持它们(大多数具有泛型的语言也不直接支持它们,因此这不是 TypeScript 的特殊缺点)。有一个开放的功能请求,microsoft/TypeScript#14466,要求这样做,但它不是 TS4.1 语言的一部分。
TypeScript 中的泛型是“通用的”,这意味着当我说class Foo<T> {...} 时,我的意思是它适用于所有 可能的类型参数T。这让Foo<T> 的消费者 指定T 的值并用它做他们想做的事,而Foo<T> 的提供者 需要允许所有可能性。
像您试图描述的那样的异构集合需要“存在”泛型。从某种意义上说,您希望interface Instruction<exists T> {...} 意味着有一个类型参数T 可以工作。这意味着Instruction 的provider 可以指定T 的值并用它做他们想做的事情,而Instruction 的consumer 需要允许所有的可能性。
有关普遍与存在量化的泛型的更多信息,请参阅this SO question and answer。
虽然 TypeScript 中没有 direct 支持存在,但有 indirect 支持。普遍和存在之间的区别与谁在看类型有关。如果你转换生产者和消费者的角色,你就会得到类似存在主义的行为。这可以通过回调来完成。所以存在词可以在 TypeScript 中编码。
让我们看看如何为Instruction 做这件事。首先,让我们将Instruction 定义为通用泛型,即您提到的“独立”版本(我将删除此代码中的JQuery 依赖项):
interface Instruction<T> {
promise: Promise<T>,
callback?: (data: T) => void
}
这是存在编码,SomeInstruction:
type SomeInstruction = <R>(cb: <T>(instruction: Instruction<T>) => R) => R;
SomeInstruction 是一个函数,它调用一个函数,该函数接受任何 T 的 Instruction<T> 并返回结果。请注意SomeInstruction 本身如何不再依赖于T。您可能想知道如何获得 SomeInstruction,但这也相当简单。让我们创建一个辅助函数,将任何Instruction<T> 转换为SomeInstruction:
const someInstruction = <T,>(i: Instruction<T>): SomeInstruction => cb => cb(i);
最后我们可以制作你的异构集合:
const arr: SomeInstruction[] = [
someInstruction({
promise: Promise.resolve({ foo: 'bar' }),
callback: (data) => console.log(data.foo)
}),
someInstruction({
promise: Promise.resolve({ bar: 'foo' }),
callback: (data) => console.log(data.bar)
})
]
根据需要进行所有类型检查。
实际上使用SomeInstruction 比使用Instruction<T> 更复杂一些,因为它需要一个回调。但这并不可怕,并且再次允许T 类型参数以消费者不知道实际T 类型是什么的方式出现,因此必须将其视为任何可能的T:
// writing out T for explicitness here
arr.forEach(someInstruction => someInstruction(<T,>(i: Instruction<T>) => {
i.promise.then(i.callback); // works
}))
// but it is not necessary:
arr.forEach(someInstruction => someInstruction(i => {
i.promise.then(i.callback); // works
}))
不错。
还有其他解决方法,但存在主义才是你真正想要的。为了完整起见,以下是一些可能的解决方法,我将提及但未实施:
-
放弃类型安全并使用any 或unknown 并使用类型断言来取回您想要的类型。布莱克。
-
使用mapped tuple type 将[T1, T2, T3] 之类的类型转换为相应的[Instruction<T1>, Instruction<T2>, Instruction<T3>] 类型。不过,这不适用于 push(),因此您也需要以某种方式解决这个问题。
-
重构 Instruction 以免需要/公开泛型。无论消费者打算用Instruction<T> 做什么,它都必须独立于T(例如,我可以写i.promise.then(i.callback),但我不能做很多其他事情,对吧?),所以创建一个Instruction 通用函数这需要一个有效的 promise-and-callback 对来创建,并返回具有您需要的任何功能的非泛型的东西,仅此而已。从某种意义上说,这是一个“精简”的存在,它不允许消费者单独访问内部的 promise-callback 对。
Playground link to code