Léal
Léal

Reputation: 61

Problem creating an event system in typescript for WebComponent

I try to implement a functional way to define WebComponent and I struggle with events. Here is what I did for the moment:

class BaseCustomComponent<Events> extends HTMLElement {
  whenEvent<K extends keyof Events>(eventName: K, callback: (event: CustomEvent<Events[K]>) => void) {
    // Do something to listen to the event
  }
}


const defineComponent = <Events>(
  setup: (context: {
    useEvents: <U>() => <K extends keyof U>(eventName: K, detail: U[K]) => void,
  }) => {
    onConnect?: () => void;
  }
) => {
  class CustomComponent extends BaseCustomComponent<Events> {
    private readonly onConnect?: () => void;

    constructor() {
      super();

      const useEvents = <U>() => {
        return <K extends keyof U>(eventName: K, detail: U[K]) => {
          // Do something to fire the event
        };
      };

      const { onConnect } = setup({ useEvents });
      this.onConnect = onConnect;
    }

    connectedCallback() {
      this.onConnect?.();
    }
  }

  // Do something to register the component

  return CustomComponent;
}

I would like to be able to "infer" the type defined inside the component:

const componentA = defineComponent(({ useEvents }) => {
  const emit = useEvents<{
    clicked: number;
  }>();

  const onConnect = () => {
    emit('clicked', 1);
    emit('clicked', '2'); // Working well: Error because the event type is number
  };

  return { onConnect };
});

const domElement = document.createElement('my-component') as InstanceType<typeof componentA>

domElement.whenEvent('clicked', ({ detail }) => console.log(detail)); // EventName is of type never

I would like to be able to infer in some way the type defined in the function defining the component so that domElement.whenEvent('clicked', /* waiting for number callback */)

I am open to any suggestions about refactoring, I would just avoid to have to specify the generic like that:

const componentA = defineComponent<{ clicked: number; }>(({ useEvents }) => {
 ...
});

Upvotes: 0

Views: 24

Answers (0)

Related Questions