#typescript #getter-setter
Вопрос:
При написании задатчика свойств в Typescript не требуется одновременно включать получатель:
set name(name: string)
{
...
}
Однако при доступе к соответствующему получателю свойств через this.name
(который не определен) это не ошибка времени компиляции и возвращает значение undefined
. Я не могу понять, почему, и я также не могу найти никаких обсуждений по этому поводу.
const theName = this.name; // undefined at runtime, but typed as 'string'
Я, скорее всего, окажусь в этой ситуации случайно, и это может отнять много времени в том редком случае, когда я не сразу замечаю.
«Традиционный» способ определения свойства таков, так что, возможно, это объясняет, почему было принято решение.
Object.defineProperty(Dog.prototype, "name", {
set: function (name) {
},
enumerable: false,
configurable: true
});
Конечно, я принимаю желаемое за действительное, но я бы предпочел, чтобы поведение заключалось в том, чтобы вернуть исходное «сырое» значение, переданное — это было бы удивительно. И избегайте этого ужасного беспорядка :-/
set name(name: string)
{
this._name = name;
doNameStuff(name);
}
get name()
{
return this._name;
}
private _name!: string;
Комментарии:
1. Поскольку TypeScript не имеет понятия
writeonly
свойств (см. microsoft/TypeScript#21759 и microsoft/TypeScript#30852 ), в настоящее время он также не позволяет утверждать, что поведение получения и установки свойства не связано по типу (см. microsoft/TypeScript#43362 ). Это в основном отсутствующая функция и, по-видимому, не имеет высокого приоритета, потому что типы только для установщиков, похоже, не очень распространены.2. @jcalz Я наткнулся на дискуссии о разных типах, но это просто похоже на отдельную ошибку во время компиляции. Почему бы не обеспечить, чтобы и то, и другое было определено? Я полагаю, они фактически говорят, что если вам действительно нужно свойство только для записи, ваш геттер автоматически нарушается — и если вы забудете или сделаете опечатку, вы сами по себе!
3. github.com/microsoft/TypeScript/issues/… имеет значение
4. @jcalz Вы можете понять, почему я не нашел этот выпуск по названию! Я тоже проверю правило линтера.
5. Полагаю, я напишу ответ, указывающий на это, если это покажется разумным.
Ответ №1:
Да, это дыра в надежности машинописного текста. Компилятор с радостью позволит вам написать set
средство доступа к свойству без соответствующего get
средства доступа, но он не очень хорошо моделирует результирующее поведение свойства. Концептуально свойство с задатчиком, но без задатчика, должно быть « writeonly
» таким же образом, как и свойство с задатчиком, но без readonly
задатчика . Действительно, если вы пишете геттер, но не сеттер, компилятор делает вывод, что свойство readonly
:
const foo = {
get bar() { return 1 }
}
/* const foo: {
readonly bar: number;
} */
Но в настоящее время нет такого понятия, как writeonly
в TypeScript, и компилятор вместо этого моделирует такое свойство как обычное свойство чтения-записи:
const baz = {
set qux(x: number) { }
}
/* const baz: {
qux: number;
} */
Существует давняя открытая проблема с запросом writeonly
свойств в microsoft/TypeScript#21759, но неясно, будет ли она когда-либо решена.
В TypeScript 4.3 появилась некоторая поддержка вариантов доступа, которая позволяет системе типов моделировать различные типы геттеров и сеттеров. Но в настоящее время существует требование, чтобы тип геттера можно было назначить типу сеттера. Вы не можете выразить, что сеттер принимает (скажем) number
, но получатель всегда производит undefined
. Итак, в microsoft/TypeScript#43662 есть еще одна открытая проблема, требующая наличия несвязанных типов в установщике и получателе. Если бы это было реализовано, это был бы еще один способ моделирования сеттеров без геттера; считывание свойства, как известно, всегда приводило undefined
бы к результату . Опять же, однако, неясно, будет ли это реализовано.
И были некоторые предложения просто сделать так, чтобы сеттеры без геттера были ошибкой компилятора, например microsoft/TypeScript#30852, но, похоже, это будет отклонено, потому что это будет критическое изменение для существующего кода TypeScript… хотя, на мой взгляд, это приведет только к нарушению кода, который уже делает странные вещи. Но я не главный.
В любом случае, как я уже упоминал в комментарии, единственное предложение, которое я мог придумать на данный момент,-это чтобы кто-то использовал правило линтера пар доступа ESLint. Это правило будет жаловаться, если вы забудете геттер, так что, по крайней мере, вам придется писать более безопасный код.
Комментарии:
1. Я думаю, что с 30852 это, вероятно, существующий код, который наверняка нарушит сделку. Но (как вы подразумевали), если вы не попытаетесь прочитать значение, то это не ошибка. Поэтому ошибка должна была возникнуть только в том случае, если вы одновременно «забыли» свой геттер И попытались его использовать. Мое любимое решение: автоматический «неявный» геттер, который просто возвращает ваше исходное «исходное» значение, не произойдет 🙁 но если бы это когда-либо произошло, это, вероятно, сделало бы многих людей очень счастливыми.
2. Я думаю, что если вы хотите увидеть такие неявные средства получения, вам следует запросить их у JavaScript, а затем TypeScript в конечном итоге получит их. TS в значительной степени выходит из игры по внедрению новых функций среды выполнения… они обожглись
enum
и больше не собираются этого делать.