Reputation: 341
I'm creating a 'Serializable' abstract class, the children of which I can create with a method call which takes a json object as the argument. I got it to work with the following code, but it results in a rather unwieldy 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;
}
I can now create a new instance of Employee 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 should be a way to not have to write 'Employee' three times in one line, but replacing anything with 'typeof this' has gotten me nowhere. I realize this could 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 this in one line, I would appreciate an example! I don't fully understand what the ctor: { new (): T}
argument does, so my ignorance of a solution might stem from this.
Upvotes: 3
Views: 60
Reputation: 164457
Yeah, you can do that:
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
Notice that there's casting of this
to any
, that's needed because Serializable
is abstract and so the compiler complains that it cannot be instantiated.
If you remove the abstract part then you can also remove this cast.
Also, there's no need to iterate over the properties like that, you can simply use Object.assign:
public static deserialize<T>(this: { new (): T }, jsonString: string): T {
return Object.assign(new (this as any)(), JSON.parse(jsonString));
}
Upvotes: 3
Reputation: 386
It is about the 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 can't rewrite the abstract class, then you are forced to use it the way you already did.
Upvotes: 0