31

I have an Object where I use it in multiple services, and each one should take some parameters, so I create two constructors, but TypeScript did not allow me to do this. My example is:

class User {
    id: number;
    username: string;
    password: string;
    email: string;
    firstName: string;
    lastName: string;
    roles: string[];

    constructor(username: string, password: string){
        this.username = username;
        this.password = password;
    }

    constructor(id: number, username: string, firstname: string, lastname: string, roles: string[]){
        this.id = id;
        this.username= username;
        this.firstname= firstname;
        this.lastname= lastname;
        this.roles = roles;
    }
    //.. and maybe another constructor also
}

Is there a trick to solve this issue, please?


When I use the optional ? in constructors for example:

constructor(
    public id?: number,
    public username?: string,
    public email?: string,
    public password?: string,
    public firstName?: string,
    public lastName?: string,
    public roles?: string[]) {
}

and when I get my data from backend:

this.service.usersList().subscribe(users => {
  console.log(users);
  this.dataSource.data = users;
});

The roles is set in the password and not in the roles failed:

{
  "id": 1,
  "username": "user1",
  "email": "[email protected]",
  "password": [
    "USER",
    "MODR"
  ]
}

For that I'm not sure about this trick.


Maybe I was not precise, but I use this method to parse my data:

static fromJson(item: Object): any {
    return new User(
        item['id'],
        item['username'],
        item['email'],
        item['roles']
    );
}

For that, when I create a constructor with optional, it will set the attributes in order of my call.

6
  • Typescript allows only 1 constructor. You can use the ? optional parameter. Which fields will be mandatory always? Commented Apr 11, 2020 at 9:29
  • @NicholasK I know that typescript allow only one constructor, but in my case I need multiple ones, and if I use ? for optional, it fail in some cases Commented Apr 11, 2020 at 9:31
  • You can use optional parameters , and verify if the needed parameters exists to implement one of the two constructors , see my answer below Commented Apr 11, 2020 at 9:31
  • Explain those cases so we can give you a better answer. Commented Apr 11, 2020 at 9:32
  • You cannot have multiple implementations of one and the same method in JS. You can however declare overload signatures for them in TypeScript, even for constructors. Commented Jun 12, 2021 at 18:36

6 Answers 6

35

You can't use multiple constructors, but you can add a few optional parameters and verify if it exists, like the following:

class User {
    id: number;
    username: string;
    password: string;
    email: string;
    firstname: string;
    lastname: string;
    roles: string[];
    // The "?" says that its optional parameter
    constructor(id?: number, username?: string, firstname?: string,
        lastname?: string, roles?: string[], password?: string) {
        if (id) { // if id exists , you can implement the first constructor
            this.id = id;
            this.username = username;
            this.firstname = firstname;
            this.lastname = lastname;
            this.roles = roles;
        }
        if (password) { // if password exists : you can implement the second one
            this.username = username;
            this.password = password;
        }
    }
}

Your response should be like this before this works fine:

static fromJson(item: Object): any {
    return new User({
        id : item['id'],
        username : item['username'],
        email : item['email'],
        roles : item['roles']
    });
}

So your constructor should be like this:

constructor(user: any){
    if (user.id) { // if id exists , you can implement the first constructor
        this.id = user.id;
        this.username = user.username;
        this.firstname = user.firstname;
        this.lastname = user.lastname;
        this.roles = user.roles;
    }
    if (user.password) { // if password exists : you can implement the second one
        this.username = user.username;
        this.password = user.password;
    }
}

Or if you don't want to do that, you can set the response regarding the order, like this:

static fromJson(item: Object): any {
    return new User(
        item['id'],
        item['username'],
        undefined,
        item['email'],
        undefined,
        undefined,
        item['roles']
    );
}
Sign up to request clarification or add additional context in comments.

10 Comments

Thank you @Aymen, When I try your solution I have the problem where roles are seted in lastName and not in roles failed { "id": 1, "username": "user1", "firstName": "[email protected]", "lastName": [ "USER", "MODR" ] }
In the backend I send an Object which hold this different attributes, but when it come to the frontend, the Object is not set correctly.
Ok, I fix it, I add the roles in the constructor
Sorry, But as I said the roles is seted in lastName and not in roles, and this I don't understand why typescript do this, It is not your mistake, but your solution is not working
I edit my question, and I add another part, maybe it can help you to understand what happen with me
|
1

I found the solution:

What happened?

When you create a constructor with optional parameters and try to call this constructor, it will set the attributes with the order of call. For that, when I call:

new User(
    item['id'],
    item['username'],
    item['email'],
    item['roles']
);

The roles in set in the firstName or password.

Solution

To solve this, it's required to change the order or parameters in the constructor:

constructor(
    public id?: number,
    public username?: string,
    public email?: string,
    public roles?: string[],

    public password?: string,
    public firstName?: string,
    public lastName?: string) {
}

Or if you won't change the order, just use undefined for example:

new User(
    item['id'],
    item['username'],
    undefined,
    item['email'],
    undefined,
    undefined,
    item['roles']
);

Until you arrive to the position of your attribute.

Comments

0

Here's a type-safe way to create multiple constructors using interfaces and discriminating unions.

It's a bit more verbose than other solutions, but it's 100% type-safe. It doesn't need to set any value to optional.

interface ShortConstructor {
  type: 'short'
  username: string
  password: string
}

interface LongConstructor {
  type: 'long'
  id: number
  username: string
  password: string
  firstName: string
  lastName: string
  roles: string[]
}

type UserConstructor = ShortConstructor | LongConstructor

class User {
  id: number
  username: string
  password: string
  email: string
  firstName: string
  lastName: string
  roles: string[]

  constructor (user: UserConstructor) {
    if (user.type === 'short') {
      this.shortConstructor(user)
    } else {
      this.longConstructor(user)
    }
  }

  shortConstructor ({ username, password }: ShortConstructor): void {
    this.username = username
    this.password = password
  }

  longConstructor ({ id, username, firstName, lastName, roles }: LongConstructor): void {
    this.id = id
    this.username = username
    this.firstName = firstName
    this.lastName = lastName
    this.roles = roles
  }
}

console.log(new User({ type: 'short', username: 'Chris', password: 'abcdef' }))
console.log(new User({
  type: 'long',
  id: 123,
  username: 'Chris',
  password: 'abcdef',
  firstName: 'AAAA',
  lastName: 'BBBB',
  roles: ['developer', 'manager']
}))

Output:

User { username: 'Chris', password: 'abcdef' }
User {
  id: 123,
  username: 'Chris',
  firstName: 'AAAA',
  lastName: 'BBBB',
  roles: [ 'developer', 'manager' ]
}

Comments

0

I had the same error while i was trying to override the default constructor to populate the fields from json data, coming from the Java world i thought this was possible with Typescript, but apparently it is not. An idea that i had is to define a static method fromData() to create a new instance, set the values from the json object and return it, example:

public static fromData(data: any): Account {
  let newInstance = new Account();
  newInstance.id = data.id
  ...
  return newInstance;
}

then in the observable i do:

subscribe({
  next: (data:any) => {
    this.account = Account.fromData(data)
  },
  error: (error:any) => console.log("Error "+error);
})

Comments

0

JavaScript does not support multiple constructors in the same way that some other object-oriented languages like Java or C# do. This is because JavaScript classes can only have one constructor method. However, you can achieve similar functionality by using default parameters, conditional logic, or factory methods within a single constructor. Here are some ways to handle multiple constructor scenarios in JavaScript:

class Car {
  private model: string;
  private year: number;

  // One constructor per class
  // Parameterized constructor 
  constructor(model, year) {
    this.model = model;
    this.year = year;
  }

  // Using factory methods
  // Default constructor
  static createWithDefault() {
    return new Car("Unknown", 0);
  }

  // Another parameterized constructor
  static createWithModel(model) {
    return new Car(model, 2025); // Default year
  }

  displayDetails() {
    console.log(`Model: ${this.model}, Year: ${this.year}`);
  }
}

// Creating objects using factory methods
let car1 = Car.createWithDefault();
let car2 = Car.createWithModel("Honda");
let car3 = new Car("Toyota", 2022);

// Displaying details of each car
car1.displayDetails(); // Output: Model: Unknown, Year: 0
car2.displayDetails(); // Output: Model: Honda, Year: 2025
car3.displayDetails(); // 

Conditional logic:

class Car {
    constructor(model, year) {
        if (typeof model === "undefined" && typeof year === "undefined") {
            this.model = "Unknown";
            this.year = 0;
        } else if (typeof year === "undefined") {
            this.model = model;
            this.year = 2025; // Default year
        } else {
            this.model = model;
            this.year = year;
        }
    }

    displayDetails() {
        console.log(`Model: ${this.model}, Year: ${this.year}`);
    }
}

// Creating objects using different constructor scenarios
let car1 = new Car();
let car2 = new Car("Honda");
let car3 = new Car("Toyota", 2022);

// Displaying details of each car
car1.displayDetails(); // Output: Model: Unknown, Year: 0
car2.displayDetails(); // Output: Model: Honda, Year: 2025
car3.displayDetails(); // Output: Model: Toyota, Year: 2022

1 Comment

Please do not post the same answer to multiple questions
-8

Use:

export class A {
   constructor() {
      // Something here
   }
   secondConstructor() {
      // Something here
      return this;
   }
}

And then you use just like this:

const a = new A();
const a2 = new A().secondConstructor();

1 Comment

An explanation would be in order. E.g., what is the idea/gist? From the Help Center: "...always explain why the solution you're presenting is appropriate and how it works". Please respond by editing (changing) your answer, not here in comments (without "Edit:", "Update:", or similar - the answer should appear as if it was written today).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.