Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: How to convert a string to a type

I am creating a bunch of web components and each of them can emit custom events. As an example here are two simple examples:

//MyButton
emits 'myButtonPressed' and detail of this type:
interface ButtonDetailType {
  id: string;
}

//MySlider
emits 'mySliderChanging' and details of this type:
interface SliderChangingDetailType {
  id: string;
  value: number;
}
emits 'mySliderChanged' and details of this type:
interface SliderChangedDetailType {
  id: string;
  oldValue: number;
  newValue: number;
}

To listen to the components I have code that looks like:

buttonEl.addEventListener( 'myButtonPressed', ( event : CustomEvent ) => {
  const detail = event.detail as ButtonDetailType;
  //now i have access to detail.id
} );

sliderEl.addEventListener( 'mySliderChanging', ( event : CustomEvent ) => {
  const detail = event.detail as SliderChangingDetailType;
  //now i have access to detail.id, detail.value
} );

As I'm making more components, I'm having difficulty remembering all the custom event names that each component can emit, or the detailType that each event generates.

To solve this problem I was hoping to create an object that contains all the information like this:

EVENTS = {
  button: {
    myButtonPressed: 'myButtonPressed',
    detailType: 'ButtonDetailType',
  },
  slider: {
    mySliderChanged': 'mySliderChanged',
    detailTypes: {
     'SliderChangedDetailType',
     'SliderChangingDetailType',
    }
  }
};

With that, I now have an easy way to access all the Event names available for each component with auto-complete helping me along the way as I type:

buttonEl.addEventListener( EVENTS.button. //autocomplete shows me all event names here! YAY!
sliderEl.addEventListener( EVENTS.slider. //autocomplete here too!

The problem that I am having is I don't know how to convert a string to a type. I'd like to be able to type this:

buttonEl.addEventListener( EVENTS.button.myButtonPressed, ( event : CustomEvent ) => {
  const detail = event.detail as EVENTS.button.detailType; // <- invalid: EVENTS.button.detailType is a string not a type!
} );

Is there a way to convert strings to types in TypeScript?

like image 763
Marek Krzeminski Avatar asked Jan 10 '19 16:01

Marek Krzeminski


Video Answer


1 Answers

You can't really convert strings to types without a mapping. In fact, you really don't want those strings in the first place except for the actual string passed into addEventListener() as the type parameter. It seems like what you actually want is something like namespaces or modules to organize your types.

For example, using namespaces we can get something similar to your EVENTS object, except that instead of referring to just string values, it refers to string values and types:

namespace EVENTS {
  export namespace button {
    export const myButtonPressed = "myButtonPressed";
    export namespace detailType {
      export interface ButtonDetailType {
        id: string;
      }
    }
  }
  export namespace slider {
    export const mySliderChanged = "mySliderChanged";
    export namespace detailTypes {
      export interface SliderChangingDetailType {
        id: string;
        value: number;
      }
      export interface SliderChangedDetailType {
        id: string;
        oldValue: number;
        newValue: number;
      }
    }
  }
}

This should give you the same autocompletes you saw before, plus ones for the types:

buttonEl.addEventListener(EVENTS.button.myButtonPressed, ((event: CustomEvent) => {
  const detail = event.detail as EVENTS.button.detailType.ButtonDetailType;
}) as EventListener);

sliderEl.addEventListener(EVENTS.slider.mySliderChanged, ((event: CustomEvent) => {
  const detail = event.detail as EVENTS.slider.detailTypes.SliderChangedDetailType;
}) as EventListener)

It's up to you if you want to change the level of nesting and naming of things since the namespace makes some things redundant (maybe you want EVENTS.slider.details.SliderChanged instead of EVENTS.slider.detailTypes.SliderChangedDetailType), but the main idea here is the general approach of using namespaces or modules.

Hope that helps; good luck!

like image 72
jcalz Avatar answered Sep 22 '22 12:09

jcalz