TypeScript and field initializers

typescript initialize class object
typescript abstract method
typescript override method
typescript constructor type
typescript interface
typescript static method
typescript lazy initialization
typescript readonly

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

[edit] When I was writing this question I was pure .NET developer without much of JS knowledge. Also TypeScript was something completely new, announced as new C#-based superset of JavaScript. Today I see how stupid this question was.

Anyway if anyone still is looking for an answer, please, look at the possible solutions below.

First thing to note is in TS we shouldn't create empty classes for models. Better way is to create interface or type (depending on needs). Good article from Todd Motto: https://ultimatecourses.com/blog/classes-vs-interfaces-in-typescript

SOLUTION 1:

type MyType = { prop1: string, prop2: string };

return <MyType> { prop1: '', prop2: '' };

SOLUTION 2:

type MyType = { prop1: string, prop2: string };

return { prop1: '', prop2: '' } as MyType;

SOLUTION 3 (when you really need a class):

class MyClass {
   constructor(public data: { prop1: string, prop2: string }) {}
}
// ...
return new MyClass({ prop1: '', prop2: '' });

or

class MyClass {
   constructor(public prop1: string, public prop2: string) {}
}
// ...
return new MyClass('', '');

Of course in both cases you may not need casting types manually because they will be resolved from function/method return type.

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 {
    first: string;
    last: string;
}
class Person {
    name: Name;
    age: number;
}

var bob: Person = {
    name: {
        first: "Bob",
        last: "Smith",
    },
    age: 35,
};

TypeScript Object Initializer Syntax · Issue #16737 · microsoft , Rationale TypeScript already gives us the ability to create re. i.e. the object initializer must contain every field that's in the object it's initializing  In C#, you’re able to initialize an object while new ing it up. However, this specific syntax is not available in TypeScript. Note that the previous code example requires that givenName and familyName must be implemented. If that’s exactly what you want, this works OK.

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.

Classes · TypeScript, ECMAScript Private Fields #. With TypeScript 3.8, TypeScript supports the new JavaScript syntax for private fields: class Animal { #name: string;  To detect the issue around accessors, TypeScript 3.7 will now emit get/set accessors in .d.ts files so that in TypeScript can check for overridden accessors. Code that’s impacted by the class fields change can get around the issue by converting field initializers to assignments in constructor bodies.

You can affect an anonymous object casted in your class type. Bonus: In visual studio, you benefit of intellisense this way :)

var anInstance: AClass = <AClass> {
    Property1: "Value",
    Property2: "Value",
    PropertyBoolean: true,
    PropertyNumber: 1
};

Edit:

WARNING If the class has methods, the instance of your class will not get them. If AClass has a constructor, it will not be executed. If you use instanceof AClass, you will get false.

In conclusion, you should used interface and not class. The most common use is for the domain model declared as Plain Old Objects. Indeed, for domain model you should better use interface instead of class. Interfaces are use at compilation time for type checking and unlike classes, interfaces are completely removed during compilation.

interface IModel {
   Property1: string;
   Property2: string;
   PropertyBoolean: boolean;
   PropertyNumber: number;
}

var anObject: IModel = {
     Property1: "Value",
     Property2: "Value",
     PropertyBoolean: true,
     PropertyNumber: 1
 };

Strict Property Initialization in TypeScript, TypeScript 2.7 introduced a new compiler option for strict property initialization has a type that includes undefined ,; has an explicit initializer, or by removing the explicit assignment to the class field and adding the public  @Elfayer TypeScript is a structural typing system, because JavaScript is largely structural. You don't need to apply a nominal constructor to some arbitrary data to have it be that type. If it is structurally the same, it is that type in TypeScript, irrespective of how it was created. Don't try to fit a square peg in a round hole. It will lead to 😭.

Below is a solution that combines a shorter application of Object.assign to more closely model the original C# pattern.

But first, lets review the techniques offered so far, which include:

  1. Copy constructors that accept an object and apply that to Object.assign
  2. A clever Partial<T> trick within the copy constructor
  3. Use of "casting" against a POJO
  4. Leveraging Object.create instead of Object.assign

Of course, each have their pros/cons. Modifying a target class to create a copy constructor may not always be an option. And "casting" loses any functions associated with the target type. Object.create seems less appealing since it requires a rather verbose property descriptor map.

Shortest, General-Purpose Answer

So, here's yet another approach that is somewhat simpler, maintains the type definition and associated function prototypes, and more closely models the intended C# pattern:

const john = Object.assign( new Person(), {
    name: "John",
    age: 29,
    address: "Earth"
});

That's it. The only addition over the C# pattern is Object.assign along with 2 parenthesis and a comma. Check out the working example below to confirm it maintains the type's function prototypes. No constructors required, and no clever tricks.

Working Example

This example shows how to initialize an object using an approximation of a C# field initializer:

class Person {
    name: string = '';
    address: string = '';
    age: number = 0;

    aboutMe() {
        return `Hi, I'm ${this.name}, aged ${this.age} and from ${this.address}`;
    }
}

// typescript field initializer (maintains "type" definition)
const john = Object.assign( new Person(), {
    name: "John",
    age: 29,
    address: "Earth"
});

// initialized object maintains aboutMe() function prototype
console.log( john.aboutMe() );

TypeScript: Where's My C# Style Object Initializers?, TypeScript: Where's My C# Style Object Initializers? April 26, 2016. In C#, you're able to initialize an object while new ing it up. using System; public class  But as Romain pointed out there is a little more info in the TypeScript 2.0 release notes: Read-only properties may have initializers and may be assigned to in constructors within the same class declaration, but otherwise assignments to read-only properties are disallowed.

TypeScript Abstract Class Guide, TypeScript and field initializers. Posted by: admin November 30, 2017 Leave a comment. Questions: How to init a new class in TS in such a way (example in C#​  However, (C#) object initializers are quite powerful syntactic sugar because they enable the ability to initialize an object using arbitrary, ad-hoc combinations of fields. They are good for 2 reasons:

TypeScript and field initializers, Preventing the use of TypeScript 'interface-style' field initializers on a class - node​.js. comp:any is needed because TypeScript compile will issue errors since Function initially does not have property name. TypeScript and field initializers. 273.

Preventing the use of TypeScript 'interface-style' field initializers on a , MakeCode programs can be authored in Blocks, Static TypeScript or Static to make them into fields); initializers for class fields; lambda functions with more  This is sometimes called “duck typing” or “structural subtyping”. In TypeScript, interfaces fill the role of naming these types, and are a powerful way of defining contracts within your code as well as contracts with code outside of your project.

MakeCode Languages: Blocks, Static TypeScript and Static Python, Objects can be initialized using new Object(), Object.create(), or using the literal notation (initializer notation). An object initializer is a  TypeScript 2.7 introduced a new compiler option for strict property initialization checks in classes. If the --strictPropertyInitialization flag is enabled, the type checker verifies that each instance property declared in a class either. has a type that includes undefined, has an explicit initializer, or.

Comments
  • The "solution" you just appended to your question is not valid TypeScript or JavaScript. But it is valuable to point out that it is the most intuitive thing to try.
  • @JacobFoshee not valid JavaScript? See my Chrome Dev Tools: i.imgur.com/vpathu6.png (but Visual Studio Code or any other TypeScript linted would inevitably complain)
  • @MichalStefanow the OP edited the question after I posted that comment. He did have return new MyClass { Field1: "ASD", Field2: "QWE" };
  • In your example, bob is not an instance of class Person. I don't see how this is equivalent to the C# example.
  • interface names are better to be started with capital "I"
  • 1. Person is not a class and 2. @JackWester is right, bob is not an instanceof Person. Try alert(bob instanceof Person); In this code example Person is there for Type Assertion purposes only.
  • I agree with Jack and Jaques, and I think its worth repeating. Your bob is of the type Person, but it is not at all an instance of Person. Imagine that Person actually would be a class with a complex constructor and a bunch of methods, this approach would fall flat on its face. It's good that a bunch of people found your approach useful, but it's not a solution to the question as it's stated and you could just as well use a class Person in your example instead of an interface, it would be the same type.
  • Why is this an accepted answer when it is clearly wrong?
  • +1. This actually creates an instance of a class (which most of these solutions don't), keeps all the functionality inside the constructor (no Object.create or other cruft outside), and explicitly states the property name instead of relying on param ordering (my personal preference here). It does lose type/compile safety between the params and properties, though.
  • @user1817787 you'd probably be better off defining anything that has a default as optional in the class itself, but assign a default. Then don't use Partial<>, just Person - that will require you to pass an object that has the required fields. that said, see here for ideas (See Pick) that limit Type Mapping to certain fields.
  • This really should be the answer. Its the best solution to this issue.
  • that is GOLDEN piece of code public constructor(init?:Partial<Person>) { Object.assign(this, init); }