Typescript中可扩展的强类型事件发射器接口

Typescript中可扩展的强类型事件发射器接口,typescript,Typescript,我在Typescript中使用强类型事件发射器接口已有一段时间了,但现在我需要它来支持向其添加自己事件的子类。有时Typescript无法意识到基类事件 以下是压缩版本()中的代码: 输入UnionToIntersection= (U扩展任何?(k:U)=>void:never)扩展((k:inferi)=>void)?I:从来没有; 类型AddParameters= ListenersT扩展(…参数:推断参数)=>void ? (event:event,…args:ArgsT)=>承诺 :从不

我在Typescript中使用强类型事件发射器接口已有一段时间了,但现在我需要它来支持向其添加自己事件的子类。有时Typescript无法意识到基类事件

以下是压缩版本()中的代码:

输入UnionToIntersection=
(U扩展任何?(k:U)=>void:never)扩展((k:inferi)=>void)?I:从来没有;
类型AddParameters=
ListenersT扩展(…参数:推断参数)=>void
? (event:event,…args:ArgsT)=>承诺
:从不;
类型签名=
{[event in keyof ListenersT]:AddParameters};
类型EmitAll=UnionToIntersection
在签名上键入=
{[event-in-keyof-ListenersT]:(event:event,listener:ListenersT[event])=>ReturnT};
欧纳尔型=
工会接口;
类型EventEmitter=EmitterInterface;
导出接口EmitterInterface
{
发射:发射全部;
关于:ONAL;
}
/////////////////////////////////////////////////////////////////////////////////////////////
接口车辆通风口
{
加速(加速:编号):无效;
制动器(减速度:个数):无效;
}
接口总线事件扩展车辆事件
{
门状态更改(前:布尔型,中:布尔型,后:布尔型):无效
}
接口车辆扩展事件发射器
{
onSig:OnSignatures;
onSigs:OnSignatures[keyof E];
}
等级车辆
{
公共构造函数()
{this.on('brake',()=>this.flashBrakeLights());}//应该工作吗?
公共flashBrakeLights():void{}
公共hitTheGas(强度:数量):无效
{this.emit('accelerate',strength*42);}//应该工作吗?
公共测试():无效
{
这就是加速;
这个大制动器;
this.onSigs('accelerate',(a)=>未定义);//错误我不明白
this.onSigs('brake',(d)=>未定义);//错误我不明白
this.onSigs('foo',()=>未定义);//假定为错误
}
}
接口总线扩展EventEmitter{}
班车延伸到汽车
{
公共门状态:[布尔,布尔,布尔]=[假,假,假];
公共构造函数()
{
超级();
this.on('加速',()=>{
此。门(0,假);
此。门(1,假);
此。门(2,假);
});
}
公共门(索引:编号,状态:布尔值):无效
{
this.doorState[索引]=状态;
this.emit('doorStateChange',…this.doorState);
}
}
导出常量总线=新总线();
E
类型被声明为
VehicleeEvents
的扩展,这应该足以让Typescript知道存在
加速
制动
事件,不是吗


有什么解释为什么这不起作用吗?有什么办法可以解决这个问题,或者用另一种方式实现我所需要的吗?

问题是,在类中,那些奇特的条件类型(不确定它们来自哪里)如果仍然包含未解析的类型参数,就无法解析。因此,虽然您使用泛型类型参数进行扩展的方法似乎是个好主意,但其效果是它使
on
emit
在类内不可用

一种解决方案是不使用类型参数,只使用事件接口本身。这样做的问题(正如您毫无疑问地发现的)是,它使类无法扩展,因为上的
emit
的任何派生版本都将与基类型版本不兼容

为了解决这个问题,我们可以使用一个函数,从基类型中删除
on
emit
。这有点老套,但我认为没有更好的办法

interface VehicleEvents {
  accelerate(acceleration: number): void;
  brake(deceleration: number): void;
}

interface BusEvents extends VehicleEvents {
  doorStateChange(front: boolean, middle: boolean, rear: boolean): void
}

interface Vehicle extends EventEmitter<VehicleEvents> {}

class Vehicle {
  public constructor() {
    this.on('brake', () => this.flashBrakeLights()); //ok 
  }

  public flashBrakeLights(): void { }

  public hitTheGas(strength: number): void { this.emit('accelerate', strength * 42); } // ok

}

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
interface Bus extends EventEmitter<BusEvents> { }

function extendEmitter<TBaseCtor extends new (...a: any[])=> any>(ctor: TBaseCtor){
  return ctor as (new (...a: ConstructorParameters<TBaseCtor>) => Omit<InstanceType<TBaseCtor>, 'on' | 'emit'>)
}
class Bus extends extendEmitter(Vehicle) {
  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();
接口车辆事件{
加速(加速:编号):无效;
制动器(减速度:个数):无效;
}
接口总线事件扩展车辆事件{
门状态更改(前:布尔型,中:布尔型,后:布尔型):无效
}
接口车辆扩展EventEmitter{}
等级车辆{
公共构造函数(){
this.on('brake',()=>this.flashBrakeLights());//好
}
公共flashBrakeLights():void{}
公共hitTheGas(强度:number):void{this.emit('accelerate',strength*42);}//ok
}
类型省略=拾取
接口总线扩展EventEmitter{}
函数扩展名any>(变量:TBASECOR){
将ctor返回为(新(…a:ConstructorParameters)=>Omit)
}
class总线扩展除雾器(车辆){
公共门状态:[布尔,布尔,布尔]=[假,假,假];
公共构造函数(){
超级();
this.on('加速',()=>{
此。门(0,假);
此。门(1,假);
此。门(2,假);
});
}
公共门(索引:编号,状态:布尔值):无效{
this.doorState[索引]=状态;
this.emit('doorStateChange',…this.doorState);
}
}
导出常量总线=新总线();
上面的版本不能确保新派生的类正确地实现基本事件。我们可以编写一个版本来验证这一点,但需要对原始定义进行一个小改动,以允许我们从基本类型中提取事件接口:

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> & {__source: ListenersT}; // do not use __source, just here to allow EventTypes to work
}

type EventTypes<T> = T extends EventEmitter<infer U> ? U : never;

/////////////////////////////////////////////////////////////////////////////////////////////

interface VehicleEvents {
  accelerate(acceleration: number): void;
  brake(deceleration: number): void;
}

interface BusEvents extends VehicleEvents {
  doorStateChange(front: boolean, middle: boolean, rear: boolean): void
}

interface Vehicle extends EventEmitter<VehicleEvents> {}

class Vehicle {
  public constructor() {
    this.on('brake', () => this.flashBrakeLights()); //ok 
  }

  public flashBrakeLights(): void { }

  public hitTheGas(strength: number): void { this.emit('accelerate', strength * 42); } // ok

}

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
interface Bus extends EventEmitter<BusEvents> { }


function extendEmitter<TBaseCtor extends new (...a: any[])=> any>(ctor: TBaseCtor){
  return function<TEvents extends EventTypes<InstanceType<TBaseCtor>>>(){
    return ctor as (new (...a: ConstructorParameters<TBaseCtor>) => Omit<InstanceType<TBaseCtor>, 'on' | 'emit'> & EventEmitter<TEvents>)
  }
}

class Bus extends extendEmitter(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();
输入UnionToIntersection=
(U扩展任何?(k:U)=>void:never)扩展((k:inferi)=>void)?I:从来没有;
类型AddParameters=
ListenersT扩展(…参数:推断参数)=>void
? (event:event,…args:ArgsT)=>承诺
:从不;
类型签名=
{[event in keyof ListenersT]:AddParameters};
类型EmitAll=UnionToIntersection
在签名上键入=
{[event-in-keyof-ListenersT]:(event:event,listener:ListenersT[event])=>ReturnT};
欧纳尔型=
工会接口;
类型EventEmitter=EmitterInterface;
导出接口EmitterInterface
{
发射:发射全部;
on:OnAll&{uuuu-source:ListenersT};//不要使用uuu-source,只在这里允许事件类型工作
}
类型EventTypes=T扩展EventEmitter?U:从来没有;
/////////////////////////////////////////////////////////////////
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> & {__source: ListenersT}; // do not use __source, just here to allow EventTypes to work
}

type EventTypes<T> = T extends EventEmitter<infer U> ? U : never;

/////////////////////////////////////////////////////////////////////////////////////////////

interface VehicleEvents {
  accelerate(acceleration: number): void;
  brake(deceleration: number): void;
}

interface BusEvents extends VehicleEvents {
  doorStateChange(front: boolean, middle: boolean, rear: boolean): void
}

interface Vehicle extends EventEmitter<VehicleEvents> {}

class Vehicle {
  public constructor() {
    this.on('brake', () => this.flashBrakeLights()); //ok 
  }

  public flashBrakeLights(): void { }

  public hitTheGas(strength: number): void { this.emit('accelerate', strength * 42); } // ok

}

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
interface Bus extends EventEmitter<BusEvents> { }


function extendEmitter<TBaseCtor extends new (...a: any[])=> any>(ctor: TBaseCtor){
  return function<TEvents extends EventTypes<InstanceType<TBaseCtor>>>(){
    return ctor as (new (...a: ConstructorParameters<TBaseCtor>) => Omit<InstanceType<TBaseCtor>, 'on' | 'emit'> & EventEmitter<TEvents>)
  }
}

class Bus extends extendEmitter(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();