'How do you create custom Event in Typescript?

How do i create a customEvent Typescript and use it? I found this link on javascript on Mozilla site (https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent)

I am just doing some testing on custom Event but Typescript see it as an error. what i am planning on doing is add some extra data into the details property to use later: here is my code.

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);


Solution 1:[1]

The property name is detail and not details. The correct code needs to be:

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);

Solution 2:[2]

Simplest way is like so:

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

Solution 3:[3]

CustomEvent is a generic type. You can pass the type of the detail property as a parameter (it defaults to any). Here is how it is defined in lib.dom.d.ts (which is in the lib directory of your npm typescript installation):

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;
}

In the OP's example, the type of detail is number. So, building on the answer from @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);

Above, I've also used the HTMLElement type from lib.dom.d.ts. As a newcomer to TypeScript, I found it very useful to scan this file, and search it for 'obvious' types.

Solution 4:[4]

The solution described in the accepted answer gets the job done but for the cost of losing type safety.

If you want to keep the type-safety going I would suggest the following:

Create dom.d.ts file in @types folder in your sources (or configure typeRoots to make sure that TS compiler will look there) with the following:

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.

This will augment global definition of document's addEventListener function to accept your synthetic event and its typed params.

now you can do:

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

This way you will have everything typed and under control.

Solution 5:[5]

I ended up taking a different approach. Instead, I made a wrapper class that extended 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);
  }
};

And usage is as follows:

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);

Solution 6:[6]

maybe overly complicated but typesafe?

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);
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Diullei
Solution 2 sidonaldson
Solution 3 Matt Wallis
Solution 4
Solution 5 Bryan McGrane
Solution 6 gman