kim*_*3er 3 arrays generics typescript
interface Instruction {
promise: Promise<unknown>,
callback?: ($html: JQuery, data: unknown ) => void
}
const arr: Instruction[] = [
{ promise: Promise.resolve({ foo: 'bar' }), callback: ($html, data) => console.log(data.foo) },
{ promise: Promise.resolve({ bar: 'foo' }), callback: ($html, data) => console.log(data.bar) }
];
Run Code Online (Sandbox Code Playgroud)
鉴于上述情况,我希望 TypeScript 能够识别回调函数中的 data 参数与 Promise 的解析类型相同。
如果它是独立的,我可以这样做:
interface Instruction<T> {
promise: Promise<T>,
callback?: ($html: JQuery, data: T) => void
}
Run Code Online (Sandbox Code Playgroud)
但是,我将如何定义数组,T每行的含义可能有所不同?
这实际上是存在泛型类型的规范用例,TypeScript 不直接支持它们(大多数具有泛型的语言也不直接支持它们,因此它不是 TypeScript 的一个特殊缺点)。有一个开放的功能请求,microsoft/TypeScript#14466,要求这样做,但它不是 TS4.1 语言的一部分。
TypeScript 中的泛型是“通用的”,这意味着当我说class Foo<T> {...}我的意思是它适用于所有可能的类型参数T。这让消费者的Foo<T>指定值T,做他们想做的事情是什么,而供应商的Foo<T>需求,允许所有的可能性。
像您试图描述的那样的异构集合需要“存在”泛型。从某种意义上说,您想interface Instruction<exists T> {...}表示它有一个适用的类型参数T。这意味着供应商的Instruction可能指定值T,做他们想做的事情是什么,而消费者的Instruction需求,以允许所有的可能性。
有关通用与存在量化泛型的更多信息,请参阅此 SO 问答。
虽然TypeScript 中没有对存在项的直接支持,但有间接支持。普遍性和存在性之间的区别与谁在看类型有关。如果你转换生产者和消费者的角色,你就会得到类似存在主义的行为。这可以通过回调来完成。因此可以在 TypeScript 中对存在项进行编码。
让我们看看我们如何为Instruction. 首先,让我们将Instruction您提到的“独立”版本定义为通用泛型(我正在删除JQuery此代码中的依赖项):
interface Instruction<T> {
promise: Promise<T>,
callback?: (data: T) => void
}
Run Code Online (Sandbox Code Playgroud)
这是存在编码,SomeInstruction:
type SomeInstruction = <R>(cb: <T>(instruction: Instruction<T>) => R) => R;
Run Code Online (Sandbox Code Playgroud)
ASomeInstruction是一个函数,它调用一个函数,该函数接受一个Instruction<T>for anyT并返回结果。注意 howSomeInstruction本身T不再依赖。你可能想知道如何得到一个SomeInstruction,但这也是相当简单的。让我们创建一个将 anyInstruction<T>转换为 a的辅助函数SomeInstruction:
const someInstruction = <T,>(i: Instruction<T>): SomeInstruction => cb => cb(i);
Run Code Online (Sandbox Code Playgroud)
最后,我们可以制作您的异构集合:
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)
})
]
Run Code Online (Sandbox Code Playgroud)
根据需要进行所有类型检查。
实际上使用aSomeInstruction比使用 an 更复杂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
}))
Run Code Online (Sandbox Code Playgroud)
好的。
还有其他变通方法,但您真正想要的是存在主义。为了完整起见,以下是我将提及但未实施的一些可能的解决方法:
放弃类型安全并使用any或unknown使用类型断言来获取您想要的类型。布莱奇。
使用映射的元组类型,您可以在其中将类型转换为[T1, T2, T3]相应的[Instruction<T1>, Instruction<T2>, Instruction<T3>]类型。但这行不通push(),所以你也需要以某种方式解决这个问题。
重构Instruction以便不需要/暴露泛型。无论消费者计划用Instruction<T>它做什么,它都必须独立于它T(例如,我可以写,i.promise.then(i.callback)但我不能做其他很多事情,对吗?),所以创建一个Instruction需要有效承诺和回调对的通用函数来创建,并返回具有您需要的任何功能的非通用内容,仅此而已。从某种意义上说,这是一个“精简”的存在,不允许消费者单独访问内部承诺-回调对。