Converting a string to a data type in Typescript: A beginner's guide

I'm currently in the process of developing various web components, each capable of emitting custom events. To illustrate, here are a couple of straightforward examples:

//MyButton
emits 'myButtonPressed' with the following details:
interface ButtonDetailType {
  id: string;
}

//MySlider
emits 'mySliderChanging' with details including:
interface SliderChangingDetailType {
  id: string;
  value: number;
}
and also emits 'mySliderChanged' with the following details:
interface SliderChangedDetailType {
  id: string;
  oldValue: number;
  newValue: number;
}

When it comes to listening for these events, my code typically resembles the following:

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 both detail.id and detail.value
} );

With an increasing number of components being developed, I've found myself struggling to keep track of all the custom event names associated with each component, as well as their respective detail types.

To address this challenge, I've been contemplating the creation of an object containing all pertinent information, like so:

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

This approach enables me to easily access all available event names for each component, with the added assistance of auto-complete functionality as I type:

buttonEl.addEventListener( EVENTS.button. //autocomplete provides a list of event names here! YAY!
sliderEl.addEventListener( EVENTS.slider. //similar autocomplete functionality here too!

However, an issue arises when attempting to convert a string into a type. Ideally, I'd be able to achieve something like this:

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

Is there a way to convert strings into types within TypeScript?

Answer №1

To convert strings to types effectively, it is essential to establish a mapping. It is advisable to avoid relying solely on strings, except when passing the actual string into addEventListener() as the type parameter. Consider utilizing namespaces or modules for organizing your types.

An example using namespaces can help create a structure similar to the EVENTS object but with references to both 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 setup allows for autocomplete functionality for both strings and 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)

You have the flexibility to adjust the nesting level and naming conventions within namespaces according to your preference. The key concept here is the utilization of namespaces or modules for effective type management.

I hope this explanation proves helpful. Best of luck!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The form control is missing a specified name attribute, causing an error with the value accessor

<input type="email" class="form-control passname" [(ngModel)]="emailID" name="Passenger Email ID" placeholder="email" required pattern="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$"/> <div class="shake-tool ...

Exploring ways to fetch an HTTP response using a TypeScript POST request

I have been looking at various questions, but unfortunately, none of them have provided the help I need. The typescript method I am currently working with is as follows: transferAmount(transfer: Transfer): Observable<number> { return this.http .po ...

How to handle form-data in NestJS Guards?

I've been trying to access form-data in my NestJS Guards, but I'm experiencing some difficulties. Despite following the tutorial provided here, I am unable to see the request body for my form-data input within the Guard itself. However, once I ac ...

The power of Ionic 2 combined with the Web Audio API

I am currently developing an Ionic 2 application that requires access to the user's microphone. When working on a web platform, I would typically use the following code snippet to obtain microphone access. navigator.getUserMedia = (navigator['ge ...

CLI package enables exporting API facilities

I have a mono repository containing a CLI package (packages/cli) and a web application (apps/web). I want to utilize the public API of the CLI within the web app. The CLI package is packaged with tsup: export default defineConfig({ clean: false, dts: ...

How can you optimize the storage of keys in JS objects?

Just pondering over this scenario: Consider a line definition like the one below, where start and end are both points. let ln = { s: {x:0, y:0}, e: {x:0, y:0}, o: 'vertical' } Now imagine having a vast array of lines, how can we sav ...

Working with relative paths in React Native TypeScript using WebStorm

My variable color is located in the path app/theme. To set it up, I created a package.json file in app/package.json with the following content: { "name": "app" } Now, to import color in TypeScript files, I use the following syntax: import { color } fro ...

Discover the combined type of values from a const enum in Typescript

Within my project, some forms are specified by the backend as a JSON object and then processed in a module of the application. The field type is determined by a specific attribute (fieldType) included for each field; all other options vary based on this ty ...

An issue occurred while attempting to retrieve Firebase data using an HTTP GET request

When trying to retrieve my data from firestore using an HTTP get request, I encountered an error. It might be helpful if my data in firestore was stored in JSON format. I'm not sure if this is feasible. <!DOCTYPE html> <html lang="en"> ...

Exploring the Integration of OverlayScrollbars with TypeScript

Currently, I am delving into TypeScript utilizing a project built on ASP.NET Core 3.0 and the VS 2019 IDE. Recently, I acquired the OverlayScrollbars plugin via npm: . npm install overlayscrollbars npm install @types/overlayscrollbar Provided below is a ...

When passing e: EventTarget as a forwarded prop through a wrapper component, Typescript raises an error about the missing "value" property in the onChange function

In my project, there is a custom component called SelectField that serves as a wrapper for rendering label, helper text, and select input (inspired by TextField from @material-UI). The SelectField component exposes props like value and onChange, which are ...

Socket.io: The other client will only update when there is interaction happening

I am currently facing a challenge setting up a real-time application using socket.io in Angular and node.js. The application is not functioning as expected. When a client makes a new post, the other clients do not receive updates until there is some inter ...

aiplafrom struggles to establish a customer using Vite alongside Vue and TypeScript

I'm currently experimenting with Gemini Pro on Vite + Vue + TS, but I encountered an issue when attempting to create an instance of PredictionServiceClient. The error message displayed is Uncaught TypeError: Class extends value undefined is not a cons ...

Incorporating yarn into your Vue3 project with Typescript

I'm attempting to implement a solution from https://yarnpkg.com/package/vue-disable-autocomplete that disables autocomplete in my Vue3 project. After running yarn add vue-disable-autocomplete, I included the following code: import DisableAutocomplete ...

Is it considered best practice to pass an argument that represents an injectable service?

Is it a good practice to pass an argument that is an injectable service to a function? Hello everyone, I have been doing some research but have not found a definitive answer to the question above yet. I am currently working with Angular and came across so ...

What is the method for filtering out specific fields in a template string?

I am currently working on defining constraints for the method field type event = { [k: `on${string}`]:(e:string)=>void } However, I need the event argument to be a number for fields that do not begin with 'on' type event = { [k: ` ...

Modifying Array Values in Angular 7

I am currently dealing with a complex array where questions and their corresponding answers are retrieved from a service. Upon receiving the array, I aim to set the 'IsChecked' attribute of the answers to false. The snippet of code I have written ...

Utilizing useClass in Angular's APP_INITIALIZER

Before my application starts up, I require some API data and am currently handling it with APP_INITIALIZER and useFactory. However, I aim to enhance the structure by separating all the code from app.module.ts: app.module.ts import { NgModule} from '@ ...

Tips for maintaining type information when using generics in constructors

class Registry<Inst, Ctor extends new (...args: unknown[]) => Inst, T extends Readonly<Record<string, Ctor>>> { constructor(public records: T) { } getCtor<K extends keyof T>(key: K) { return this.records[key] } getIns ...

Creating React components with TypeScript: Defining components such as Foo and Foo.Bar

I have a react component defined in TypeScript and I would like to export it as an object so that I can add a new component to it. interface IFooProps { title:string } interface IBarProps { content:string } const Foo:React.FC<IFooProps> = ({ ...