Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript and field initializers

Tags:

typescript

How to init a new class in TS in such a way (example in C# to show what I want):

// ... some code before return new MyClass { Field1 = "ASD", Field2 = "QWE" }; // ...  some code after 
like image 825
Nickon Avatar asked Jan 03 '13 15:01

Nickon


People also ask

What are field Initializers?

Fields are initialized immediately before the constructor for the object instance is called. If the constructor assigns the value of a field, it will overwrite any value given during field declaration. For more information, see Using Constructors. A field initializer cannot refer to other instance fields.

What is field in TypeScript?

But before we get into the main content, we need to clarify the concept of fields. A field is a variable declared directly in a class. See the article about constructors to understand how TypeScript assigns values to fields: TypeScript Constructors.

How do you initialize a complex 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", };

How do I initialize a new object in TypeScript?

Initialize Typed variable to an Empty Object in TypeScript # Use type assertions to initialize a typed variable to an empty object, e.g. const a1 = {} as Animal; . You can then set the properties on the object using dot or bracket notation. All of the properties you set on the object need to conform to the type.


2 Answers

Updated 07/12/2016: Typescript 2.1 introduces Mapped Types and provides Partial<T>, which allows you to do this....

class Person {     public name: string = "default"     public address: string = "default"     public age: number = 0;      public constructor(init?:Partial<Person>) {         Object.assign(this, init);     } }  let persons = [     new Person(),     new Person({}),     new Person({name:"John"}),     new Person({address:"Earth"}),         new Person({age:20, address:"Earth", name:"John"}), ]; 

Original Answer:

My approach is to define a separate fields variable that you pass to the constructor. The trick is to redefine all the class fields for this initialiser as optional. When the object is created (with its defaults) you simply assign the initialiser object onto this;

export class Person {     public name: string = "default"     public address: string = "default"     public age: number = 0;      public constructor(         fields?: {             name?: string,             address?: string,             age?: number         }) {         if (fields) Object.assign(this, fields);     } } 

or do it manually (bit more safe):

if (fields) {     this.name = fields.name || this.name;            this.address = fields.address || this.address;             this.age = fields.age || this.age;         } 

usage:

let persons = [     new Person(),     new Person({name:"Joe"}),     new Person({         name:"Joe",         address:"planet Earth"     }),     new Person({         age:5,                        address:"planet Earth",         name:"Joe"     }),     new Person(new Person({name:"Joe"})) //shallow clone ];  

and console output:

Person { name: 'default', address: 'default', age: 0 } Person { name: 'Joe', address: 'default', age: 0 } Person { name: 'Joe', address: 'planet Earth', age: 0 } Person { name: 'Joe', address: 'planet Earth', age: 5 } Person { name: 'Joe', address: 'default', age: 0 }    

This gives you basic safety and property initialization, but its all optional and can be out-of-order. You get the class's defaults left alone if you don't pass a field.

You can also mix it with required constructor parameters too -- stick fields on the end.

About as close to C# style as you're going to get I think (actual field-init syntax was rejected). I'd much prefer proper field initialiser, but doesn't look like it will happen yet.

For comparison, If you use the casting approach, your initialiser object must have ALL the fields for the type you are casting to, plus don't get any class specific functions (or derivations) created by the class itself.

like image 163
Meirion Hughes Avatar answered Oct 27 '22 01:10

Meirion Hughes


Update

Since writing this answer, better ways have come up. Please see the other answers below that have more votes and a better answer. I cannot remove this answer since it's marked as accepted.


Old answer

There is an issue on the TypeScript codeplex that describes this: Support for object initializers.

As stated, you can already do this by using interfaces in TypeScript instead of classes:

interface Name {     givenName: string;     surname: string; } class Person {     name: Name;     age: number; }  var bob: Person = {     name: {         givenName: "Bob",         surname: "Smith",     },     age: 35, }; 
like image 34
Wouter de Kort Avatar answered Oct 27 '22 01:10

Wouter de Kort