Инициализация значений класса Typescript из конструктора
Я использую TypeScript для создания некоторых классов с KnockoutJS, с данными, загружаемыми из некоторого JSON, возвращаемого WebAPI.
Проблема в том, что я хотел скопировать значения JSON в мой класс TypeScript из конструктора: но если я делаю это только в базовом классе, унаследованные свойства не были определены и поэтому не инициализированы.
пример
Мы хотим создать элемент инвентаря из ответа JSON:
{ Name: "Test", Quantity:1, Price: 100 }
У меня есть базовый класс Product и унаследованный класс Inventory:
export class Product {
Name = ko.observable("");
constructor(source) {
// a utility that copies properties into this instance
utils.CopyProperties(source,this);
}
export class Inventory extends Product {
Quantity = ko.observable(0);
Price = ko.observable(0);
constructor(source) {
super(source); // call base c'tor
// the quantity and price properties are only now defined
}
}
Свойства для Inventory создаются только в выходном коде JS после вызова супер-конструктора, поэтому не существуют при выполнении конструктора Product.
Единственное решение, которое я вижу, - это извлечь инициализирующее значение из конструктора, но мне не очень нравится этот подход, хотя я подозреваю, что это единственный вариант.
var inventoryItem = new Inventory();
inventoryItem.LoadFrom(source);
Ответы
Ответ 1
Лучшее, что я могу придумать, чтобы позволить вам иметь базовую процедуру десериализации, вызываемую из конструктора, - это (изменено для удаления зависимости от нокаута для тестирования):
class utils {
public static CopyProperties(source:any, target:any):void {
for(var prop in source){
if(target[prop] !== undefined){
target[prop] = source[prop];
}
else {
console.error("Cannot set undefined property: " + prop);
}
}
}
}
class Product {
Name = "Name";
constructor(source) {
this.init(source);
}
init(source){
utils.CopyProperties(source,this);
}
}
class Inventory extends Product {
Quantity;
Price;
constructor(source) {
super(source);
}
init(source){
this.Quantity = 0;
this.Price = 0;
super.init(source);
}
}
var item = new Inventory({ Name: "Test", Quantity: 1, Price: 100 });
Нечетно, что переменные инициализируются только в JS после вызова super()
. Возможно, стоит поднять рабочий элемент на codeplex?
Playground.
Ответ 2
Этот подход работает для меня:
/// <reference path="knockout.d.ts" />
export class Product {
Name: KnockoutObservableString;
constructor(source) {
this.Name = ko.observable(source.Name);
}
}
export class Inventory extends Product {
Quantity: KnockoutObservableNumber;
Price: KnockoutObservableNumber;
constructor(source) {
super(source);
this.Quantity = ko.observable(source.Quantity);
this.Price = ko.observable(source.Price);
}
}
var item = new Inventory({ Name: "Test", Quantity: 1, Price: 100 });
Ответ 3
@JcFx
эта переменная test всегда неопределена до того, как ей присвоено значение.
if(target[prop] !== undefined){
Вы можете сделать это, если выражение всегда 'true', или использовать это вместо:
for (const prop of Object.keys(source)) {
this[prop] = source[prop];
}
это о forin, см. эту ссылку: https://github.com/angular/tsickle/issues/125