【问题标题】:How do you create custom Event in Typescript?如何在 Typescript 中创建自定义事件?
【发布时间】:2022-03-18 10:08:27
【问题描述】:

如何创建 customEvent Typescript 并使用它?我在 Mozilla 网站 (https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent) 上的 javascript 上找到了这个链接

我只是对自定义事件进行一些测试,但 Typescript 将其视为错误。我打算做的是在 details 属性中添加一些额外的数据以供以后使用:这是我的代码。

let div:any=document.getElementById("my_div");

let c_event = new CustomEvent("build",{'details':3 });

div.addEventListener("build",function(e:Event){
    console.log(e.details);
}.bind(this));

div.dispatchEvent(c_event);

【问题讨论】:

  • 您看到了什么错误?这看起来像有效的 js 代码,因此很可能也是有效的 ts。
  • 让 c_event = new CustomEvent("build",{'details':3 });应该是 let c_event = new CustomEvent("build",{detail:3 });

标签: typescript


【解决方案1】:

属性名称是detail,而不是details。正确的代码需要是:

let div: any = document.getElementById("my_div");

let c_event = new CustomEvent("build",{detail: 3});

div.addEventListener("build", function(e: CustomEvent) { // change here Event to CustomEvent
    console.log(e.detail);
}.bind(this));

div.dispatchEvent(c_event);

【讨论】:

  • 起初我认为这不起作用,但后来我发现 CustomEvent 上的 JSON.stringify() 没有显示详细成员......叹息。
  • 看到这个:has no properties in common with type 'CustomEventInit<unknown>'
  • 它对我有用,只有当我将addEventListener 中的事件参数类型定义为CustomEventInit 时
  • CustomEvent 是泛型类型,所以在本例中,detail 的类型为number,您可以使用CustomEvent<number>。见答案stackoverflow.com/a/62568282/685715
【解决方案2】:

最简单的方法是这样的:

window.addEventListener("beforeinstallprompt", ((event: CustomEvent) => {
   console.log("Whoop!");
}) as EventListener);

【讨论】:

    【解决方案3】:

    CustomEvent 是一个泛型类型。您可以将detail 属性的类型作为参数传递(默认为any)。以下是它在lib.dom.d.ts 中的定义(位于 npm typescript 安装的lib 目录中):

    interface CustomEvent<T = any> extends Event {
        /**
         * Returns any custom data event was created with. Typically used for synthetic events.
         */
        readonly detail: T;
        initCustomEvent(typeArg: string, canBubbleArg: boolean, cancelableArg: boolean, detailArg: T): void;
    }
    

    在 OP 的示例中,detail 的类型是 number。因此,基于@Diullei 的答案:

    let div: HTMLElement | null = document.getElementById("my_div");
    
    let c_event = new CustomEvent<number>("build", {detail: 3});
    
    div.addEventListener("build", function(e: CustomEvent<number>) { // change here Event to CustomEvent
        console.log(e.detail);
    }.bind(this));
    
    div.dispatchEvent(c_event);
    

    上面,我还使用了lib.dom.d.ts 中的HTMLElement 类型。 作为 TypeScript 的新手,我发现扫描这个文件并搜索“明显”类型非常有用。

    【讨论】:

    • 如果您将 index.d.ts 添加到您的根目录,它会在 REACT 中自动获取。我还添加了声明 CustomEvent 和接口 CustomEventInit
    【解决方案4】:

    接受的答案中描述的解决方案可以完成工作,但代价是失去类型安全。

    如果您想保持类型安全,我建议如下:

    在您的源代码中的@types 文件夹中创建dom.d.ts 文件(或配置typeRoots 以确保TS 编译器会在那里查看),并使用以下内容:

    interface CustomEventMap {
        "customnumberevent": CustomEvent<number>;
        "anothercustomevent": CustomEvent<CustomParams>;
    }
    declare global {
        interface Document { //adds definition to Document, but you can do the same with HTMLElement
            addEventListener<K extends keyof CustomEventMap>(type: K,
               listener: (this: Document, ev: CustomEventMap[K]) => void): void;
        }
    }
    export { }; //keep that to TS compliler.
    
    

    这将增加document 的addEventListener 函数的global definition 以接受您的合成事件及其类型参数。

    现在你可以这样做了:

    function onCustomEvent(event: CustomEvent<CustomParams>){
      this.[...] // this is Document
      event.detail ... //is your CustomParams type.
    }
    document.addEventListener('anothercustomevent', onCustomEvent);
    

    这样您就可以输入并控制所有内容。

    【讨论】:

      【解决方案5】:

      我最终采取了不同的方法。相反,我创建了一个扩展 EventTarget 的包装类。

      type AudioEvent = {bytes: Uint8Array};
      
      interface IAudioEventTarget {
        addListener(callback: (evt: CustomEvent<AudioEvent>) => void): void
        dispatch(event: AudioEvent): boolean;
        removeListener(callback: (evt: CustomEvent<AudioEvent>) => void): void
      }
      
      class AudioEventTarget extends EventTarget implements IAudioEventTarget {
        private readonly targetType = 'audio-event';
      
        addListener(callback: (evt: CustomEvent<AudioEvent>) => void): void {
          return this.addEventListener(this.targetType, callback as (evt: Event) => void);
        }
      
        dispatch(event: AudioEvent): boolean {
          return this.dispatchEvent(new CustomEvent(this.targetType, { detail: event }));
        }
      
        removeListener(callback: (evt: CustomEvent<AudioEvent>) => void): void {
          return this.removeEventListener(this.targetType, callback as (evt: Event) => void);
        }
      };
      

      而用法如下:

      const audioEventTarget = new AudioEventTarget();
      const listener = (audioEvent: CustomEvent<AudioEvent>) => {
          console.log(`Received ${audioEvent.detail.bytes.length} bytes`);
      }
      
      audioEventTarget.addListener(listener);
      audioEventTarget.dispatch({bytes: new Uint8Array(10)});
      audioEventTarget.removeListener(listener);
      

      【讨论】:

        【解决方案6】:

        可能过于复杂但类型安全?

        interface FizzInfo {
          amount: string;
        }
        
        interface BuzzInfo {
          level: number;
        }
        
        interface FizzBuzzEventMap {
          fizz: CustomEvent<FizzInfo>;
          buzz: CustomEvent<BuzzInfo>;
        }
        
        interface FizzerBuzzer extends EventTarget {
          addEventListener<K extends keyof FizzBuzzEventMap>(type: K, listener: (this: FizzerBuzzer, ev: FizzBuzzEventMap[K]) => void, options?: boolean | AddEventListenerOptions): void;
          addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
          removeEventListener<K extends keyof FizzBuzzEventMap>(type: K, listener: (this: FizzerBuzzer, ev: FizzBuzzEventMap[K]) => void, options?: boolean | EventListenerOptions): void;
          removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
        }
        
        class FizzerBuzzer extends EventTarget {
          numFizz: number = 0;
          numBuzz: number = 0;
        
          start(): void {
            setInterval(() => this.emitFizz(), 3000);
            setInterval(() => this.emitBuzz(), 5000);
          }
          emitFizz(): void {
            ++this.numFizz;
            this.dispatchEvent(new CustomEvent<FizzInfo>('fizz', {
              detail: { amount: this.numFizz.toString() },
            }));
          }
          emitBuzz(): void {
            ++this.numBuzz;
            this.dispatchEvent(new CustomEvent<BuzzInfo>('buzz', {
              detail: { level: this.numBuzz },
            }));
          }
        }
        
        
        const fb = new FizzerBuzzer();
        fb.addEventListener('fizz', (ev) => {
          console.assert(typeof ev.detail.amount === 'string', 'bad!');
          console.log(ev.detail.amount);
        });
        fb.addEventListener('buzz', (ev) => {
          console.assert(typeof ev.detail.level === 'number', 'bad');
          console.log(ev.detail.level);
        });
        
        

        【讨论】:

          猜你喜欢
          • 2019-01-11
          • 1970-01-01
          • 2012-07-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-11
          • 2020-02-05
          相关资源
          最近更新 更多