【发布时间】:2021-12-30 00:15:39
【问题描述】:
大家好 StackOverflowers!
我已经尝试了很长一段时间(使用更复杂的代码)来实现一个通用类型安全的回调系统,在该系统中,人们可以注册某个事件的回调,并且这个回调被添加到特定事件类型的侦听器数组中.
当我不使用数组时它工作正常,但每个事件只有一个回调。但是当我尝试使用数组而不是将回调推送到其中时,打字稿会丢失。
enum MyEvent {
One,
Two
}
type Callback<T> = (arg: T) => void;
type CallbackTypes = {
[MyEvent.One]: number
[MyEvent.Two]: string
}
class CallbackContainer {
callbacksA: { [E in MyEvent]: Callback<CallbackTypes[E]> } = {
[MyEvent.One]: () => {},
[MyEvent.Two]: () => {}
};
callbacksB: { [E in MyEvent]: Callback<CallbackTypes[E]>[] } = {
[MyEvent.One]: [],
[MyEvent.Two]: []
};
constructor() {}
setListenerA<E extends MyEvent, C extends typeof this.callbacksA[E]>(this: CallbackContainer, event: E, callback: C) {
this.callbacksA[event] = callback; // =)
}
getListenerA<E extends MyEvent>(event: E) {
return this.callbacksA[event];
}
setListenerB<E extends MyEvent, C extends typeof this.callbacksB[E][number]>(this: CallbackContainer, event: E, callback: C) {
const callbacks = this.callbacksB[event]; // type of callbacks is still intact
callbacks.push(callback); // callbacks collapsed
}
getListenersB<E extends MyEvent>(event: E) {
return this.callbacksB[event];
}
}
const c = new CallbackContainer();
c.setListenerA(MyEvent.One, (a) => a + 1); // Types resolve fine
c.getListenerA(MyEvent.One); // Types resolve fine
c.setListenerB(MyEvent.One, (a) => a + 1); // Types resolve fine
c.getListenersB(MyEvent.One); // Types resolve fine
将回调推入数组时,我得到:
Argument of type 'Callback<number> | Callback<string>' is not assignable to parameter of type 'Callback<number> & Callback<string>'.
Type 'Callback<number>' is not assignable to type 'Callback<number> & Callback<string>'.
Type 'Callback<number>' is not assignable to type 'Callback<string>'.
Type 'string' is not assignable to type 'number'.ts(2345)
在 push 之前,编译器完全知道 C 的类型(回调是):
C extends {
0: Callback<number>[];
1: Callback<string>[];
}[E][number]>
回调数组的类型是:
const callbacks = {
0: Callback<number>[];
1: Callback<string>[];
}[E]
但是当pushing 时,它们分别崩溃为Callback<number> | Callback<string> 和 Callback<number>[] | Callback<string>[]。我是否遇到了打字稿编译器的限制,或者我是否遗漏了一些明显的东西?如果这是一个限制,是否有任何解决方法?谢谢!
【问题讨论】:
-
调用签名
setListener<E extends MyEvent, C extends typeof this.callbacksB[E][number]>(this: CallbackContainer, event: E, callback: C)比这里看起来需要的要复杂得多;setListener<E extends MyEvent>(event: E, callback: Callback<CallbackTypes[E]>)对你有用吗?在我写的答案中,我会假设是这样。 -
setListener<E extends MyEvent>(event: E, callback: Callback<CallbackTypes[E]>)也可以。我只是想让编译器工作,所以typeof this部分是一种反复试验。
标签: typescript generics