创建动态事件处理程序类型

时间:2018-11-09 15:20:50

标签: typescript inheritance mapped-types

我希望扩展我的组件类的所有对象都可以使用类型检查事件处理程序,使用方法名称确定它们处理的事件类型。

class UIEvent { }
class MouseInput extends UIEvent { }
class KeyboardInput extends UIEvent { }

let Events = {
    MouseInput,
    KeyboardInput,
}

然后,我使用映射类型创建一个EventTarget类型,该类型定义了从名称到事件处理程序类型的映射。

type EventTarget = {
    [K in keyof typeof Events]?:
      (event: InstanceType<typeof Events[K]>) => void;
}

EventTarget可以根据需要处理任意数量的事件类型,因此事件处理程序方法是可选的。

然后我试图在组件类中使用它,但是我无法让编译器强制执行该接口。

class Component implements EventTarget {
    // The compiler has no problems with this, even though it violates
    // the EventTarget interface...
    MouseInput(event: KeyboardInput) {

    }
}

我强烈怀疑我在这里试图反对结构化类型,因为一旦我添加了一个使MouseEventKeyboardEvent不同的属性,类型检查就会起作用。

class UIEvent<T> { t: T }
class MouseInput extends UIEvent<"MouseInput"> {}
class KeyboardInput extends UIEvent<"KeyboardInput"> {}

这可行,但我宁愿使用事件的构造函数作为判别器,而不是添加不同的属性类型。

最后,我想将此合同扩展到组件类的子类型。

class Clickable extends Component {
    // This should also fail to typecheck, but interfaces aren't inherited
    MouseInput(event: KeyboardInput) {

    }
}

我现在能得到的最接近的结果是让每个子类型显式实现EventTarget

1 个答案:

答案 0 :(得分:1)

您是对的,您在此反对结构化键入。只要这些类没有成员来区分它们,编译器就会认为它们是兼容的。

我认为您的设计可能会包含一些属性,这些属性将使这些特性超出此简单的概念证明。因此,这将已经产生预期的错误:

export class UIEvent { }
class MouseInput extends UIEvent { public x!: number; public y!: number }
class KeyboardInput extends UIEvent { public key!: string }

let Events = {
    MouseInput,
    KeyboardInput,
}

type EventTarget = {
    [K in keyof typeof Events]?:
      (event: InstanceType<typeof Events[K]>) => void;
}

class Component implements EventTarget {
    MouseInput(event: KeyboardInput) { // Error now 

    }
}

如果您不想公开任何成员,则可以声明类型为private的{​​{1}}成员。这将使类型不兼容,但不会对anu运行时产生影响:

undefined
相关问题