В Typescript, почему мне нужно объявлять свойство вне конструктора класса, в отличие от Javascript, где мне не нужно было бы этого делать?

#javascript #typescript #oop

Вопрос:

В Javascript я мог бы создать класс, как показано ниже, то есть мне не нужно было бы объявлять и инициировать свойство, вызываемое logs вне конструктора:

 class Logger {
  constructor() {
    this.logs = [];
  }
}
 

Однако в машинописном виде это дало бы мне ошибку:
Property 'logs' does not exist on type 'Logger'.ts(2339)
Чтобы исправить это, я объявил журналы свойств следующим образом: logs: Array<any>;
Псевдокод в машинописном тексте без ошибок выглядит так:

 class Logger {
  logs: Array<any>;

  constructor() {
    this.logs = [];
  }
}
 

Это, скорее всего, вопрос для начинающих с машинописным текстом. Однако я просмотрел документы и другие ресурсы и не смог найти объяснения.
Поскольку я знаю, как исправить, меня больше интересуют объяснения, различия и лучшие практики.

Комментарии:

1. TS — это безопасность типа . Если вы попытаетесь присвоить свойство, которое, по мнению TS, не должно существовать, это остановит вас. Потому что это похоже на ошибку. И в вашем коде вообще нет ничего, что указывало бы на то, что это logs допустимое свойство в вашем классе. Следовательно, TS делает вывод, что он не должен существовать.

2. Я думаю, что это хороший вопрос, но я не знаю, как получить на него канонический ответ. microsoft/TypeScript#37920 (выпущен с TS4.0) реализовал нечто подобное для проверки файлов JS, но вам все равно нужно объявить свойства класса в файлах TS. Если бы был авторитетный ответ, это было бы какое-то документированное решение команды TS о том, почему компилятор не делает вывод о существовании свойства из его использования; легко предположить, что ему не удастся поймать слишком много реальных ошибок, но я не знаю, действительно ли это проблема.

3. Ах, вот он, microsoft/TypeScript#766 , с этим комментарием, в частности , о том, почему все так, как есть. Может быть, я изложу это в ответе, когда у меня будет такая возможность

Ответ №1:

«Почему» — это сложно. В этом вопросе есть две части

1. Зачем нужно определение типа?

ВЛАЗ уже рассказал об этом в своем комментарии. TypeScript недостаточно для определения типа, поэтому ему нужны более четкие указания.

«Конечно, TS может понять, что это массив!», однако это правда…

2. Почему определение типа необходимо вверху?

Конструктор в этом случае все путает, но давайте возьмем немного другой класс:

 class Logger { 
  a() {
    this.logs = 1234;
  }

  b() {
    this.logs = ['a', 'b']
  }
}
 

Из вышесказанного, что это за тип logs ? Это зависит от обстоятельств, верно? Если a() вызывается первым, то это число, в противном случае это массив. «зависит» на самом деле недостаточно хорошо, чтобы дать нам сильную уверенность в наших типах. Поэтому (я думаю) машинопись вынуждает вас объявлять ее в одном центральном месте — в верхней части класса, вне какого-либо метода.

Вы также можете спросить, почему он не может сделать вывод из конструктора? Это всегда будет происходить до a() или b() .

Я подозреваю, что это связано с тем, что поля могут быть установлены во время их объявления, например:

 class Logger {
  logs: Array<string> = ['a', 'b'];
  
  constructor() {
    this.logs = 6611; // <-- wait a second...
  }
}