Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to build a TypeScript class constructor with object defining class fields?

Tags:

typescript

In TypeScript it's possible to create a class with a constructor that takes parameter with access modifiers and it automatically convert those parameters in class fields.

class Item {   constructor(     public id: number,     public updatedAt: number,     public createdAt: number,   ) {} }  const item = new Item(1, 1, 1); item.id // 1 

I'm wondering if there is a way to pass all those parameters in an object instead

class Item {   constructor({     public id: number,     public updatedAt: number,     public createdAt: number,   }) {} }  const item = new Item({ id: 1, updatedAt: 1, createdAt: 1 }); item.id // 1 

Is this possible? Ever gonna be possible?

Are there workarounds to do something similar?

like image 540
BrunoLM Avatar asked Mar 02 '18 02:03

BrunoLM


People also ask

How do I create a constructor for a class in TypeScript?

The TypeScript docs have a great example of constructor usage: class Greeter { greeting: string; constructor(message: string) { this. greeting = message; } greet() { return "Hello, " + this. greeting; } } let greeter = new Greeter("world");

How class constructor are defined in TypeScript with example?

Example: Declaring a class The var keyword is not used while declaring a field. The example above declares a constructor for the class. A constructor is a special function of the class that is responsible for initializing the variables of the class. TypeScript defines a constructor using the constructor keyword.

How do you initialize a class object in TypeScript?

To initialize an object in TypeScript, we can create an object that matches the properties and types specified in the interface. export interface Category { name: string; description: string; } const category: Category = { name: "My Category", description: "My Description", };

Does a TypeScript class need a constructor?

Classes in TypeScript do not require you to explicitly write a constructor. However if you are extending a base class you will need to create a constructor to call super() at a minimum.


1 Answers

The simplest way would be to declare the fields in the class and use a mapped type as a parameter, then use Object.assign to assign the fields to this. We have several options for which mapped type to use:

Partial<T>

Type will contain all members (fields and methods) of the class but all of them are optional. The disadvantage here is we can't make some of the fields required, and the caller can potentially override a method

class Item {      public id: number;     public updatedAt: number;     public createdAt: number;     constructor(data: Partial<Item>) {         Object.assign(this, data);     }     method() {} }  //Works  const item = new Item({ id: 1, updatedAt: 1, createdAt: 1 }); //This also works unfortunately  const item2 = new Item({ id: 1, method() { console.log('overriden from param !')} }); 

Pick<T, K>

This mapped type allows us to pick some properties from T by specifying a union of several string literal types that are keys of T. The advantages are that Pick will inherit whether the field is required or not from the original declaration in the class (so some fields can be required and other optional) and since we specify which members we pick, we can omit methods. The disadvantage is that we have to write property names twice (once in the class and once in the Pick):

class Item {     public id: number;     public updatedAt?: number;     public createdAt?: number;     constructor(data: Pick<Item, "id" | "updatedAt" | "createdAt">) {         Object.assign(this, data);     }     method() {} } const item = new Item({ id: 1  }); //id is required others fields are not const item2 = new Item({ id: 1, method() {}  }); // error method is not allowed 

Custom Mapped Type That Removes Methods

The third option would be create a type similar to Pick that includes all class fields but not the methods automatically. We can do this in Typescript 2.8 using conditional types (unrelease at the time of writing, but should be release in March 2018, you can get it right now via npm install -g typescript@next). This has the advantages of Pick without the need to specify filed names again:

type NonMethodKeys<T> = ({[P in keyof T]: T[P] extends Function ? never : P } & { [x: string]: never })[keyof T];   type RemoveMethods<T> = Pick<T, NonMethodKeys<T>>;   class Item {     public id: number;     public updatedAt?: number;     public createdAt?: number;     constructor(data: RemoveMethods<Item>) { // No need to specify field names again         Object.assign(this, data);     }     method() {} }  const item = new Item({ id: 1  });  //id is required others fields are not const item2 = new Item({ id: 1, method() {}  }); // error method is not allowed  
like image 134
Titian Cernicova-Dragomir Avatar answered Oct 06 '22 03:10

Titian Cernicova-Dragomir