Creating a TypeScript class that includes an input interface with the ability to have optional

Struggling with the compiler here and wondering if I'm on the wrong track or just pursuing a misguided approach.

My goal is to have a class with required parameters, along with an input interface containing optional parameters. If a parameter is missing from the Input, the class constructor should default to a sensible value.

interface PersonInput {
  name?: string;
  age?: number;
}

class Person {
  name: string;
  age: number;
  constructor(input: PersonInput) {
    this.name = "Zach";
    this.age = 30;
    for (const key in input) {
      this[key] = input[key]; // <--- Running into errors here
    }
  }
}
// Error: TS7053: Element implicitly has an 'any' type because 
// expression of type 'string' can't be used to index type 'PersonInput'.

If I ensure that the element doesn't have an any type associated with it:

\\ ...snip
    for (const key in input) {
      const personParam: keyof Person = key; // <-- Type 'string | number | undefined' is not assignable to type 'never'
      this[personParam] = input[personParam];
    }
\\...snip

To work around this, I opted not to spread properties and instead did something like this:

//...snip
class Person {
  name: string;
  age: number;
  constructor(input: PersonInput) {
    this.name = input.name || "Zach";
    this.age = input.age || 30;
  }
}

What am I missing?

Addendum

I've come across the param! syntax - is that relevant in this case? I don't think so since the loop only executes if the parameter has been defined, rather than relying on a property being passed in the input.

Answer №1

To streamline the process of assigning default values to properties in a class, you can avoid manual assignments by setting the defaults upfront and using Object.assign(target, source) within the constructor.

If your interface consists of the original type Person with optional properties, TypeScript offers a helper type called Partial<T> that eliminates the need for an additional interface like PersonInput. Simply use Partial<Person> wherever necessary without duplicating the structure.

Combining these suggestions results in:

class Person {
  name: string = "Zach";
  age: number = 30;

  constructor(input: Partial<Person>) {
    Object.assign(this, input);
  }
}

However, if you prefer the for loop method, you must provide additional instructions to the compiler for indexing types safely.

class Person {
  name: string = "Zach";
  age: number = 30;
  constructor(input: Partial<Person>) { // or keep your PersonInput type
    for (const key in input) {
      // cast instead of just declaring the type
      const personParam = key as keyof Person; 
      // 'as any' allows safe indexing of types not normally allowed by the compiler
      (this as any)[personParam] = input[personParam];
    }
  }
}

It's worth noting that passing undefined as a value for properties will result in those properties being set to undefined, such as new Person({ name: undefined }). Therefore, new Person({}) is different from

new Person({ name: undefined, age: undefined })
.

Try running the above code on the TS Playground

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

Mapping intricate entities to intricate DTOs using NestJS and TypeORM

Currently, I am using the class-transformer's plainToClass(entity, DTO) function to transform entities into DTO objects. In addition, I have implemented the transform.interceptor pattern as outlined in this source. I make use of @Expose() on propert ...

Implement dynamic changes to CSS variable values using Typescript during runtime

My angular 4 web application requires dynamic styling based on color values and image URLs received from the backend. While I have successfully set up the app to load images from the server, handling color values poses a greater challenge. The primary colo ...

Ways to trigger child components function from parent component

I am working with a parent-child component setup. In the child component (child.component.ts), there is a method called "childFunction()". Now, I need to call this method from within a function in the parent component. Can you guide me on how to achieve ...

NestJS - The GraphQL schema was expected to be defined, but was found to be

I've been working on setting up a small GraphQL API using NestJS 8. I made sure to install all the required dependencies as per the documentation. However, upon starting the server, I am encountering this error: [Nest] 22727 - 10/30/2021, 10:11:10 AM ...

The operation to assign a value to property 'two' cannot be completed as it is currently undefined

I'm facing an issue with the code below and cannot figure out why I am encountering the error message. I have ensured that each object contains a value, so why is there a reference to 'undefined'? Cannot set property 'two' of unde ...

Creating an Angular service that checks if data is available in local storage before calling an API method can be achieved by implementing a

I am currently working on developing an Angular service that can seamlessly switch between making actual API calls and utilizing local storage within a single method invocation. component.ts this.userService.getAllUsers().subscribe(data => { conso ...

Converting a JSON array into a TypeScript array

Looking to convert a JSON array into a TypeScript variable array. The JSON data retrieved from http://www.example.com/select.php: { "User":[ {"Name":"Luca M","ID":"1"}, {"Name":"Tim S","ID":"2"}, {"Name":"Lucas W","ID":"3"} ...

To properly format the date value from the ngModel in Angular before sending it to the payload, I require the date to be in the format

When working with Angular 9, I am facing an issue where I need to format and send my date in a specific way within the payload. Currently, the code is sending the date in this format: otgStartDate: 2021-07-20T09:56:39.000Z, but I actually want it to be for ...

Error Message: The specified HTML element already contains two instances of the WebViewer, leading to a conflict in PDFTron React TypeScript Next

Having some trouble using pdftron with my docx editor. I can use the editor fine, but keep encountering an error like the one shown below: https://i.stack.imgur.com/OnJxE.png https://i.stack.imgur.com/l9Oxt.png Here is a snippet of my code: wordeditor.t ...

Limiting the parameter type in Node.js and TypeScript functions

Currently working on a Node.js project utilizing TypeScript. I'm attempting to limit the argument type of a function to a specific base class. As a newcomer to both Node and TypeScript with a background in C#, I may not fully grasp some of the langua ...

Attempting to implement a typeguard in Typescript that relies on the presence of specific content within an element

Currently, I am attempting to develop a Typescript conditional that verifies if a particular word is already present in the name. The function in question is as follows: isOrganic() { for (let i = 0; i < this.items.length; i++) { if(this.ite ...

What is the best way to disable a collapsed section in VS Code using comments?

I'm wondering how to properly comment out a "folded" section of code using VS Code. For instance, I want to comment out the collapsible region: if (a == b) { dance(); } I am familiar with the keyboard shortcut for folding regions: Ctrl + Shift + ...

Form submission returns JSON data with an undefined value from the server

I've been following a tutorial here but ran into some issues due to using newer versions of Angular and Ionic. This is an excerpt from my code: createReview(review){ let headers = new HttpHeaders(); headers.append('Content-Type&apo ...

Error: Unable to retrieve options using this.getOptions function. This issue is unrelated to Vue, it is occurring within

Required Modules "dependencies": { "express": "^4.17.1", "express-static-gzip": "^2.1.1", "react": "^17.0.2", "react-dom": "^17.0.2", "reac ...

What is the best way to determine the type of `rootReducer`?

My project is set up with a combination of React, Redux, Immutable.js, and TypeScript. As I worked on implementing it, I made an effort to declare types wherever possible which led me to discover an interesting issue. A code example illustrating the proble ...

Pause for Progress - Angular 6

I'm looking for a solution to solve the following issue. I need to access a property that will store data from an http request. Therefore, I want to verify this property only after the transaction is completed. validateAuthorization(path: string): ...

I'm unable to import correctly using the --compiler option

Having an issue here. I'm trying to bring in the typescript compiler. I used this command: bit import bit.envs/compilers/typescript --compiler Unfortunately, it didn't work. This is the error message: bit import [ids...] import components in ...

Is it possible to preserve the spoken "text" through Ionic Speech Recognition for future reference?

After successfully implementing Ionic Speech Recognition in my project using the Ionic framework documentation, I am now facing a challenge with saving the text or audio input using any form input method like ngmodel or formcontrol. My attempts to bind th ...

Place an image at the top of the canvas at a specific location

Currently, I am in the process of reconstructing this specific website My approach involves working with React (similar to the aforementioned site) and utilizing the same cropper tool that they have implemented. For cropping, I am incorporating react-imag ...

Obtaining a phone number from a contact in Nativescript Angular: A step-by-step guide

Upon executing the following code: let desiredFields = ['display_name','phone','thumbnail','email','organization']; console.log('Loading contacts...'); let timer = new Date().getTime(); Contact ...