Allowing the use of a string as a parameter in a Typescript constructor

Currently, I am utilizing TypeScript to create a constructor for a model within Angular. One of the attributes in the model is configured as an enum with specific string values. Everything functions well if an enum value is passed to the constructor. The issue lies in the fact that I must invoke the constructor based on input from an API response, which supplies a string that needs to be mapped to that particular property.

Is there a method where I can pass a string (as long as it corresponds to one of the values defined in the enum) to the constructor?

Enum:

export enum TestTypes {
    FIRST = 'first',
    SECOND = 'second',
}

export class Test {
    this.myType: TestTypes;

    constructor(options: {type: TestTypes}) {
        this.myType = options.type;
    }
}

The following approach is effective

const a = new Test({type:TestTypes.FIRST});

My desired outcome:

const b = new Test({type:'first'})

Should I implement the following?

const b = new Test({type:TestTypes['first']})

Answer №1

An efficient solution is to modify your enum into a direct dictionary structure like the following:

const literal = <L extends string | number | boolean>(l: L) => l;

export const TestTypes = {
  FIRST: literal('first'),
  SECOND: literal('second'),
};

export type TestTypes = (typeof TestTypes)[keyof typeof TestTypes]

The literal() function acts as a helper that instructs the compiler to interpret the value as a string literal rather than expanding it to string.

Now, TestTypes.FIRST holds the exact value of the string "first", TestTypes.SECOND contains the exact string "second", and the type TestTypes represents the union "first"|"second". This configuration allows your class to perform as intended:

export class Test {
  myType: TestTypes; // note that this is an annotation, not an initializer

  constructor(options: { type: TestTypes }) {
    // use options.type instead of just type  
    this.myType = options.type;
  }
}

const a = new Test({ type: TestTypes.FIRST }); // runs smoothly
const b = new Test({ type: "first" }); // also works fine as they are equivalent

If you prefer keeping TestTypes as an enum, achieving your objective is feasible but involves complex steps.

To begin with, if you aim for a standalone function that accepts either the enum or the correct string values, you can create a generic function as shown below:

declare function acceptTestTypesOrString<E extends string>(
  k: E & (Extract<TestTypes, E> extends never ? never : E)
): void;

This implementation leverages the fact that TestTypes.FIRST extends "first". Let's test its functionality:

acceptTestTypesOrString(TestTypes.FIRST) // valid
acceptTestTypesOrString(TestTypes.SECOND) // acceptable
acceptTestTypesOrString("first") // works fine
acceptTestTypesOrString("second") // no issues
acceptTestTypesOrString("third") // triggers an error

The results look promising. However, transforming this into a constructor function poses a challenge since constructor functions cannot be generic. In this scenario, making the entire class generic seems like a suitable alternative:

cclass Test<E extends string> {
  myType: E; // remember, this is an annotation, not an initializer

  constructor(options: { 
    type: E & (Extract<TestTypes, E> extends never ? never : E) 
  }) {
    // assign options.type instead of type  
    this.myType = options.type;
  }
}

const a = new Test({ type: TestTypes.FIRST }); // operational
const b = new Test({ type: "first" }); // effective as well

In this setup, a will correspond to type Test<TestTypes.FIRST> while b will align with Test<"first">. Although they are largely interchangeable, introducing a generic type for the entire class when it is needed solely for the constructor may seem suboptimal.

Nevertheless, this approach delivers the desired outcome.


Hopefully, one of these suggestions proves useful. 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

Tips for successfully clearing the localStorage in an Ionic2 application upon exiting

Could someone please provide guidance on how to detect when the application is being exited using the hardware back button and then clear my localStorage data? I have three main reasons for needing this functionality: 1. Prompt the user to confirm if they ...

Where is the best place to import styles in NextJS?

I have an existing project with a file importing the following function import theme from 'src/configs/theme' However, when I created a new Next.js project and replicated the same folder structure and imported the same function, it is showing "m ...

The submit button is getting disrupted by the background image being set

Implement the following CSS code to set a background image on a single-page app (Angular). .page::before { content: ''; position: absolute; background-size: cover; width: 100%; height: 100%; background-image: url("../../assets/weird. ...

Issue with Angular: Unable to locate a differ that supports the object '[object Object]' of type 'object'. NgFor is only compatible with binding to Iterables such as Arrays

As someone who is new to Angular, I am facing a challenge while working on my portfolio project. The issue arises when trying to receive a list of nested objects structured like this: "$id": "1", "data": { &quo ...

How to Import External Libraries into Angular 2

I am attempting to import a 3rd party library called synaptic. This library is written in JavaScript and I have a .d.ts file for it. Here are the steps I have taken: npm install synaptic --save npm install @types/synaptic --save I have also added the fol ...

Http service not found

I am facing a problem with injecting HTTP into my Angular 2 application. Everything was working smoothly a few days ago, but now I am encountering this error: ORIGINAL EXCEPTION: No provider for Http! Here is the code snippet from main.ts: import { pl ...

Error: Module 'redux/todo/actions' could not be located. Please check the file path

Despite reading numerous posts and articles on getting absolute imports to work in a TypeScript project, I have not encountered any issues with this. My project is functioning properly with absolute imports. However, within VS Code, all absolute imports a ...

The boolean variable is returning as undefined after being called from a different component

There are 2 parts - the header and login components. If the loginU function returns true, I want to display a logout button in the header component. However, in the console, it shows that the boolean value is undefined in the header component. Login Compo ...

Ways to incorporate padding into md-card using Angular 2 material

Need assistance with adding padding to md-card in angular2 material. I am using md-card to display my product list but struggling to add padding on them. Here's what I have tried: product.component.html : <p> Product List </p> <div ...

Move your cursor over a div element while utilizing NgClass

I'm currently working on implementing a hover effect on a div using an Angular ngClass directive. Within the template file, I have a div element with the class "list-item-container" that includes another div with the class "list-item." This "list-item ...

What are the best techniques for streamlining nested objects with Zod.js?

As a newcomer to zod.js, I have found that the DataSchema function is extremely helpful in verifying API data types and simplifying the API response easily. However, I'm curious if there is a way to streamline the data transformation process for myEx ...

Transmitting a base64 data URL through the Next.js API route has proven to be a challenge, but fortunately, other forms of

It's frustrating me to no end. I've successfully done this before without any problems, but now it just won't cooperate. Everything works fine when passing an empty array, a string, or a number. However, as soon as I include the data URL, t ...

What is the best approach for looping through a JSON object?

Currently constructing a project using Angular and incorporating redux. In my JSON object, there are nested objects with specific values. Let's imagine: name: "john", sex: "m" children: [ { name: "joe", sex: "m" children: [ { name: " ...

The partial template is not functioning as anticipated

Introducing an interface designed to accept two templates, with the resulting function being a partial of one of them (similar to React-Redux): export type IState<TState, TOwnProps> = { connect: (mapStateToProps: MapStateToProps<TState, Parti ...

typescript throwing an unexpected import/export token error

I'm currently exploring TypeScript for the first time and I find myself puzzled by the import/export mechanisms that differ from what I'm used to with ES6. Here is an interface I'm attempting to export in a file named transformedRowInterfac ...

Is there a way to use Lodash to quickly return the same value if a condition is met using the ternary operator

Is there a condensed way to do this using Lodash? (or perhaps Vanilla JS/TypeScript) var val = _.get(obj, 'value', ''); Please note that var val = obj.value || ''; is not suitable as it considers 0 and false as valid but fal ...

Utilizing and transmitting contextual information to the tooltip component in ngx-bootstrap

I am currently working on integrating tooltips in ngx-bootstrap and trying to figure out how to pass data to the ng-template within the tooltip. The documentation mentions using [tooltipContext], but it doesn't seem to be functioning as expected. Belo ...

Utilizing Regex to Authenticate a CAGE Code

Our task is to create a RegEx pattern that can accurately validate a CAGE Code A CAGE Code consists of five (5) positions. The code must adhere to the following format: The first and fifth positions must be numeric. The second, third, and fourth position ...

Tips on adjusting the dimensions of a switch in kendo UI angular with CSS

Currently, I am utilizing angular in combination with Kendo ui. Is there a way to modify the dimensions of the switch button in Kendo UI Angular using CSS? ...

Struggling to retrieve the header in Angular 6

When setting headers from an Express server written in NodeJS, I use the following code: app.use('/routes', function(req, res, next) { res.setHeader('test', 'test'); next(); ); The headers are successfully sent to th ...