#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...
}
}