Подтвердить что ты не робот

Инициализация значений класса 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);
4b9b3361

Ответ 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