【发布时间】:2019-09-09 20:01:23
【问题描述】:
我在 Typescript 中使用强类型事件发射器接口已经有一段时间了,但现在我需要它来支持向其添加自己的事件的子类。在某些时候,Typescript 无法识别基类事件。
这是精简版的代码 (Playground Link):
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
type AddParameters<ListenersT, EventT> =
ListenersT extends (...args: infer ArgsT) => void
? (event: EventT, ...args: ArgsT) => Promise<boolean>
: never;
type EmitSignatures<ListenersT> =
{ [EventT in keyof ListenersT]: AddParameters<ListenersT[EventT], EventT> };
type EmitAll<ListenersT> = UnionToIntersection<EmitSignatures<ListenersT>[keyof ListenersT]>
type OnSignatures<ListenersT, ReturnT> =
{ [EventT in keyof ListenersT]: (event: EventT, listener: ListenersT[EventT]) => ReturnT };
type OnAll<ListenersT, ReturnT> =
UnionToIntersection<OnSignatures<ListenersT, ReturnT>[keyof ListenersT]>;
type EventEmitter<ListenersT> = EmitterInterface<ListenersT>;
export interface EmitterInterface<ListenersT>
{
emit: EmitAll<ListenersT>;
on: OnAll<ListenersT, this>;
}
/////////////////////////////////////////////////////////////////////////////////////////////
interface VehicleEvents
{
accelerate(acceleration: number): void;
brake(deceleration: number): void;
}
interface BusEvents extends VehicleEvents
{
doorStateChange(front: boolean, middle: boolean, rear: boolean): void
}
interface Vehicle<E extends VehicleEvents> extends EventEmitter<E>
{
onSig: OnSignatures<E, this>;
onSigs: OnSignatures<E, this>[keyof E];
}
class Vehicle<E extends VehicleEvents>
{
public constructor()
{ this.on('brake', () => this.flashBrakeLights()); } // supposed to work?
public flashBrakeLights(): void {}
public hitTheGas(strength: number): void
{ this.emit('accelerate', strength * 42); } // supposed to work?
public test(): void
{
this.onSig.accelerate;
this.onSig.brake;
this.onSigs('accelerate', (a) => undefined); // error I don't understand
this.onSigs('brake', (d) => undefined); // error I don't understand
this.onSigs('foo', () => undefined); // supposed to error
}
}
interface Bus extends EventEmitter<BusEvents> {}
class Bus extends Vehicle<BusEvents>
{
public doorState: [boolean, boolean, boolean] = [false, false, false];
public constructor()
{
super();
this.on('accelerate', () => {
this.door(0, false);
this.door(1, false);
this.door(2, false);
});
}
public door(index: number, state: boolean): void
{
this.doorState[index] = state;
this.emit('doorStateChange', ...this.doorState);
}
}
export const bus = new Bus();
E 类型被声明为 VehicleEvents 的扩展,这应该足以让 Typescript 知道有 accelerate 和 brake 事件,不是吗?
任何解释为什么这不起作用?有关如何解决此问题或以其他方式实现我需要的任何想法?
【问题讨论】:
-
在
Vehicle内部,问题是E是未知的,所以所有条件和映射类型的魔法都不能完全解决,所以你会得到错误。此外,onSigs是签名的联合,因此即使从外部也不会像您期望的那样工作(参数必须是所有可能性的交集。 -
是的
E在Vehicle内部是未知的,但它是知道它扩展了VehicleEvents。所以我认为 Typescript 甚至没有尽可能地解决它。考虑到onSigs,我也想到了这一点,但是第一个参数不应该是所有事件名称的联合,因此也会出错(它只抱怨我的处理程序部分)?另外,有什么想法可以实现我的需要吗? :( -
我会考虑的 .. 我刚才回答了一个类似的问题 stackoverflow.com/questions/50369299/…,但它也不是可扩展的 .. 我尝试了几件事,但没有让它工作不幸的是你想要的方式
-
sub-events 是一个完全可扩展的替代方案;)如果您想了解详细信息,请询问,我会发布详细答案。
标签: typescript