Как унаследоваться от Date? (typescript)

371
27 июля 2017, 00:20

Вот вроде-бы логичный вариант:

class MyDate extends Date {
    constructor(dateTimeStr: string) {
        super(dateTimeStr);
        console.log(this, typeof(this));
        this.setTime(this.getTime() + 1000);
    }        
}

Но оказывается, что, по всей видимости, от того, что super вызывает Date как функцию Date(), а не как класс new Date, то Date возвращает строку. Соответственно и this становится строкой.

Подскажите как нормально можно расширить Date.

Answer 1

Приведенный пример кода работает, если используется синтаксис ES6. Отличие от typescript в данном случае только в отсутствии типа параметра, передаваемого в конструктор.

Это возможно, так как класс в разделе спецификации о Date есть следующие строки:

The Date constructor is designed to be subclassable. It may be used as the value of an extends clause of a class definition. Subclass constructors that intend to inherit the specified Date behaviour must include a super call to the Date constructor to create and initialize the subclass instance with a [[DateValue]] internal slot.

Конструктор Date определен так, что возможно создавать подклассы. Он может использоваться как значение для extends в определении класса. Конструкторы подклассов, которые должны наследовать поведение Date должны включать вызов super, для создания и инициализации внутреннего слота [[DateValue]].

Убедиться, что все работает, можно на примере:

class MyDate extends Date { 
  constructor(dateTimeStr) { 
    super(dateTimeStr); 
    console.log(this, typeof(this)); 
    this.setTime(this.getTime() + 1000); 
  } 
} 
 
var d = new MyDate("2017-07-26T00:00:00.000Z"); 
 
console.log(d.getDate()); // 26

Почему не работает в typescript, и при ручной реализации?

Проблема заключается во внутреннем слоте [[DateValue]], к которому внутри обращаются все функции Date.

Поэтому следующий пример будет кидать исключение:

function MyDate(dateTimeStr) { 
  Date.call(this, dateTimeStr); 
  this.setTime(this.getTime() + 1000); 
} 
 
MyDate.prototype = Object.create(Date.prototype); 
 
var d = new MyDate("2017-07-26T00:00:00.000Z");

Реализация, которая идет в typescript несколько сложнее

function MyDate(dateTimeStr) {
    var _this = _super.call(this, dateTimeStr) || this;
    console.log(_this, typeof (_this));
    _this.setTime(_this.getTime() + 1000);
    return _this;
}

В данном случае переписывается this, который будет возвращен из конструктора, и вызов _super.call(this, dateTimeStr) в данном случае эквивалентен вызову Date.call(this, dateTimeStr), что просто вернет строку.

Как решить?

Победить эту проблему может помочь ключ компиляции -target, если в нем указать ES6 и выше.

В случае ручной реализации, в похожем вопросе советуют создавать Date и менять ему прототип, например так:

function MyDate(dateTimeStr) { 
  var _this = new Date(dateTimeStr); 
  _this.setTime(_this.getTime() + 1000); 
  Object.setPrototypeOf(_this, MyDate.prototype); 
  return _this; 
} 
 
MyDate.prototype = Object.create(Date.prototype); 
MyDate.prototype.foo = function(){ 
  return `${this.getFullYear()}-${this.getMonth()+1}`; 
} 
 
var d = new MyDate("2017-07-26T00:00:00.000Z"); 
console.log(d.getDate()); // 26 
console.log(d instanceof Date); // true 
console.log(d instanceof MyDate); // true 
console.log(d.foo()); //2017-7 

READ ALSO
Как сделать, чтобы babel работал с файлами, подключаемыми динамически?

Как сделать, чтобы babel работал с файлами, подключаемыми динамически?

Подключая файлы динамически через роутинг webpack:

267
Как сбросить эффект от Pinch в js?

Как сбросить эффект от Pinch в js?

Собственно вопрос: В мобильном браузере(например safari под ios), при Pinch-е происходит увеличение страницыНужно сбросить этот эффект по требованию...

298
Запутался в JS скрипте, как лучше сделать?

Запутался в JS скрипте, как лучше сделать?

Сам скрипт https://jsfiddlenet/oyhvy9wf/

213
Canvas заполнение поля двухмерных массивом

Canvas заполнение поля двухмерных массивом

Здравствуйте, нашел такой код, пытаюсь разобраться в нем, смысл в чем, создаётся некий квадрат (300 на 300 пикселей) и заполняется двухмерным...

285