Is there a clean way to create static inherited shared constructors in TypeScript?
I am creating an abstract Serializable class whose children I can create by calling a method that takes a json object as an argument. I got it to work with the following code, but it leads to a rather cumbersome solution.
My current code:
abstract class Serializable {
public static deserialize<T>(jsonString: string, ctor: { new (): T}) {
const newCtor = new ctor;
const jsonObject = JSON.parse(jsonString);
for (const propName of Object.keys(jsonObject)) {
newCtor[propName] = jsonObject[propName]
}
return newCtor;
}
public static deserializeList<T>(jsonString: string, ctor: { new (): T}) {
let newCtor = new ctor;
const newArray = new Array<typeof newCtor>();
const jsonArray = JSON.parse(jsonString)['staff'];
for (const jsonObject of jsonArray) {
newCtor = new ctor;
for (const propName of Object.keys(jsonObject)) {
newCtor[propName] = jsonObject[propName]
}
newArray.push(newCtor);
}
return newArray;
}
}
export class Employee extends Serializable {
firstName: string;
lastName: string;
}
Now I can create a new Employee instance like this:
const testJson = '{"firstName": "Max", "lastName": "Mustermann"}';
const testEmployee = Employee.deserialize<Employee>(testJson, Employee);
Ideally, I would like to be able to do this:
const testJson = '{"firstName": "Max", "lastName": "Mustermann"}';
const testEmployee = Employee.deserialize(testJson);
I feel like there must be a way not to write "Employee" three times on the same line, but replacing something with "typeof this" got me nowhere. I understand that this can be avoided by not making the constructor static, but instead having two lines:
const testJson = '{"firstName": "Max", "lastName": "Mustermann"}';
const testEmployee = new Employee();
testEmployee.deserialize(testJson);
But if there is any clean way to do it in one line, I would appreciate an example! I don't quite understand what the argument is doing ctor: { new (): T}
, so my ignorance of the solution may be related to this.
source to share
Yes, you can do this:
abstract class Serializable {
public static deserialize<T>(this: { new(): T }, jsonString: string): T {
const newCtor = new (this as any)();
...
}
}
const testEmployee = Employee.deserialize2(testJson); // type of testEmployee is Employee
Note that there was a drop this
before any
, which is necessary because it Serializable
is abstract, so the compiler complains that it cannot be instantiated.
If you remove the abstract part, you can also remove this listing.
Also, there is no need to iterate over properties like this, you can just use Object.assign :
public static deserialize<T>(this: { new (): T }, jsonString: string): T {
return Object.assign(new (this as any)(), JSON.parse(jsonString));
}
source to share
It's about type.
abstract class Serializable {
public static deserialize(jsonString: Employee) {
const newCtor = new Employee();
const jsonObject = JSON.parse(jsonString);
for (const propName of Object.keys(jsonObject)) {
newCtor[propName] = jsonObject[propName]
}
return newCtor;
}
public static deserializeList(jsonString: Employee) {
let newCtor = new Employee();
const newArray: Employee[] = [];
const jsonArray = JSON.parse(jsonString)['staff'];
for (const jsonObject of jsonArray) {
newCtor = new ctor;
for (const propName of Object.keys(jsonObject)) {
newCtor[propName] = jsonObject[propName]
}
newArray.push(newCtor);
}
return newArray;
}
}
If you cannot rewrite an abstract class, you are forced to use it the way you already did.
source to share