Typescript中泛型函数的映射

Typescript中泛型函数的映射,typescript,Typescript,我有一个消息接口,它接受一个通用参数T,并将T的值设置为一个名为body的内部属性的类型: 接口消息{ 正文:T } 我还有一个EventHandler接口,它描述了一个通用函数,该函数将消息作为其唯一参数: 接口事件处理程序{ (事件:消息):无效 } 最后,我有一个Integration类,它有一个subscribe方法和一个subscriptions映射,这两个映射都如下所示: 类集成{ 公共订阅:新地图() 受保护的订阅(eventName:string,处理程序:EventHand

我有一个
消息
接口,它接受一个通用参数
T
,并将
T
的值设置为一个名为
body
的内部属性的类型:

接口消息{
正文:T
}
我还有一个
EventHandler
接口,它描述了一个通用函数,该函数将
消息
作为其唯一参数:

接口事件处理程序{
(事件:消息):无效
}
最后,我有一个
Integration
类,它有一个
subscribe
方法和一个
subscriptions
映射,这两个映射都如下所示:

类集成{
公共订阅:新地图()
受保护的订阅(eventName:string,处理程序:EventHandler){
this.subscriptions.set(事件名称,处理程序)
}
}
最终,我希望用户能够定义他们自己的
集成
实例,如下所示:

interface MyEventProperties {
  foo: string
}

class MyIntegration extends Integration {
   constructor() {
     super()
     this.subscribe('My Event Name', this.myEventHandler)
   }
   myEventHandler(event: Message<MyEventProperties>) {
     // do something
   }
}

我的问题是,我是在错误地处理这个问题,还是遗漏了更明显的东西?

简单的解决方案是使用
unknown
作为映射中的通用参数。以下解决方案将起作用:

interface Message<T> {
    body: T
}
interface EventHandler<T> {
    (event: Message<T>): void
}

class Integration {
    public subscriptions = new Map<string, EventHandler<unknown>>()
    protected subscribe<T>(eventName: string, handler: EventHandler<T>) {
        this.subscriptions.set(eventName, handler)
    }
    public publishEvent<T>(eventName: string, msg: Message<T>) {
        (this.subscriptions.get(eventName) as EventHandler<T>)(msg);
    }
}

interface MyEventProperties { foo: string }
class MyIntegration extends Integration {
    constructor() {
        super()
        this.subscribe('My Event Name', this.myEventHandler)
        this.subscribe('My Event Name Mistaken', this.myEventHandler) // no relation between name and event 
    }
    myEventHandler(event: Message<MyEventProperties>) {
        // do something
    }
}
接口消息{
正文:T
}
接口事件处理程序{
(事件:消息):无效
}
类集成{
公共订阅=新地图()
受保护的订阅(eventName:string,处理程序:EventHandler){
this.subscriptions.set(事件名称,处理程序)
}
public publishEvent(eventName:string,msg:Message){
(this.subscriptions.get(eventName)作为EventHandler)(msg);
}
}
接口MyEventProperties{foo:string}
类MyIntegration扩展了集成{
构造函数(){
超级()
this.subscribe('My Event Name',this.myEventHandler)
this.subscribe('My Event Name missed',this.myEventHandler)//名称和事件之间没有关系
}
myEventHandler(事件:消息){
//做点什么
}
}
但问题是事件名称和事件类型之间没有关系。这可能是订阅时(如上所述)以及发出事件时(不会根据预期的消息类型检查消息正文)都存在的问题

我建议在事件类型和名称之间添加一个映射类型:

interface Message<T> {
    body: T
}
interface EventHandler<T> {
    (event: Message<T>): void
}

class Integration<TEvents> {
    public subscriptions = new Map<PropertyKey, EventHandler<unknown>>()
    protected subscribe<K extends keyof TEvents>(eventName: K, handler: EventHandler<TEvents[K]>) {
        this.subscriptions.set(eventName, handler)
    }
    public publishEvent<K extends keyof TEvents>(eventName: K, msg: Message<TEvents[K]>) {
        (this.subscriptions.get(eventName) as EventHandler<TEvents[K]>)(msg);
    }
}

interface MyEventProperties { foo: string }

class MyIntegration extends Integration<{
    'My Event Name': MyEventProperties
}> {
    constructor() {
        super()
        this.subscribe('My Event Name', this.myEventHandler)
        this.subscribe('My Event Name Mistaken', this.myEventHandler) // error now
        this.subscribe('My Event Name', (e: Message<string>) => { }) // error, type of body not ok  
    }
    myEventHandler(event: Message<MyEventProperties>) {
        // do something
    }
}

let m = new MyIntegration();
m.publishEvent("My Event Name", 0) // error body not ok 
m.publishEvent("My Event Name M") // error mistaken name
m.publishEvent("My Event Name", {
    body: { foo: ""}
}) // ok message and body correct
接口消息{
正文:T
}
接口事件处理程序{
(事件:消息):无效
}
类集成{
公共订阅=新地图()
受保护的订阅(eventName:K,处理程序:EventHandler){
this.subscriptions.set(事件名称,处理程序)
}
public publishEvent(事件名称:K,消息:Message){
(this.subscriptions.get(eventName)作为EventHandler)(msg);
}
}
接口MyEventProperties{foo:string}
类MyIntegration扩展了集成{
构造函数(){
超级()
this.subscribe('My Event Name',this.myEventHandler)
this.subscribe('我的事件名称错了',this.myEventHandler)//现在出错
this.subscribe('My Event Name',(e:Message)=>{})//错误,正文类型不正确
}
myEventHandler(事件:消息){
//做点什么
}
}
设m=newmyintegration();
m、 publishEvent(“我的事件名称”,0)//错误体不正常
m、 publishEvent(“我的事件名称m”)//错误名称
m、 publishEvent(“我的事件名称”{
正文:{foo:}
})//ok消息和正文正确

为没有泛型类型参数的
EventHandler
创建基类。在您的
subscriptions
集合中使用此选项,在public
subscribe
方法中使用
EventHandler
选项。另一种可能是在EventHandler中将泛型默认为“从不”或“未知”,这样就不再需要传入它,不过我只是浏览了本文。接口EventHandler@AndyLamb-那么你是说
映射
构造函数是这样的<代码>新映射无效>()(它可以接受任何参数作为参数)?谢谢你,提香!这非常有用。我有一些类似于您设计的
TEvents
界面的东西,所以我在这方面做得很好。不过,还有一个问题是,我试图强制执行
Message
中的泛型参数扩展了另一种特定类型。所以声明看起来像是
接口消息{}
。我认为这使得不可能将
EventHandler
定义为
EventHandler
对吗?但我认为可以执行
EventHandler
interface Message<T> {
    body: T
}
interface EventHandler<T> {
    (event: Message<T>): void
}

class Integration<TEvents> {
    public subscriptions = new Map<PropertyKey, EventHandler<unknown>>()
    protected subscribe<K extends keyof TEvents>(eventName: K, handler: EventHandler<TEvents[K]>) {
        this.subscriptions.set(eventName, handler)
    }
    public publishEvent<K extends keyof TEvents>(eventName: K, msg: Message<TEvents[K]>) {
        (this.subscriptions.get(eventName) as EventHandler<TEvents[K]>)(msg);
    }
}

interface MyEventProperties { foo: string }

class MyIntegration extends Integration<{
    'My Event Name': MyEventProperties
}> {
    constructor() {
        super()
        this.subscribe('My Event Name', this.myEventHandler)
        this.subscribe('My Event Name Mistaken', this.myEventHandler) // error now
        this.subscribe('My Event Name', (e: Message<string>) => { }) // error, type of body not ok  
    }
    myEventHandler(event: Message<MyEventProperties>) {
        // do something
    }
}

let m = new MyIntegration();
m.publishEvent("My Event Name", 0) // error body not ok 
m.publishEvent("My Event Name M") // error mistaken name
m.publishEvent("My Event Name", {
    body: { foo: ""}
}) // ok message and body correct