Reputation: 59950
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.
Upvotes: 31
Views: 45597
Reputation: 4335
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
Upvotes: 0
Reputation: 2066
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);
})
Upvotes: 0
Reputation: 1187
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' ]
}
Upvotes: 0
Reputation: 179
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();
Upvotes: -8
Reputation: 59950
I found the solution:
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.
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.
Upvotes: 1
Reputation: 1915
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']
);
}
Upvotes: 35