В Typescript, что это такое! (восклицательный знак / удар) оператор при разыменовании члена?


458

Просматривая исходный код правила tslint, я наткнулся на следующее утверждение:

if (node.parent!.kind === ts.SyntaxKind.ObjectLiteralExpression) {
    return;
}

Обратите внимание на !оператора после node.parent. Интересно!

Сначала я попытался скомпилировать файл локально с моей текущей установленной версией TS (1.5.3). Полученная ошибка указала на точное местоположение взрыва:

$ tsc --noImplicitAny memberAccessRule.ts 
noPublicModifierRule.ts(57,24): error TS1005: ')' expected.

Затем я обновился до последней версии TS (2.1.6), которая скомпилировала его без проблем. Так что, похоже, это особенность TS 2.x. Но транспиляция полностью проигнорировала взрыв, что привело к следующему JS:

if (node.parent.kind === ts.SyntaxKind.ObjectLiteralExpression) {
    return;
}

Мой гугл фу пока что подвел меня.

Что такое оператор восклицательного знака TS и как он работает?

Ответы:


696

Это ненулевой оператор утверждения. Это способ сказать компилятору «это выражение не может быть nullили undefinedздесь, поэтому не жалуйтесь на возможность того, что оно есть nullили undefined». Иногда средство проверки типов не может сделать это определение самостоятельно.

Это объясняется здесь :

Новый !оператор выражения после исправления может использоваться, чтобы утверждать, что его операнд не равен нулю и не определен в контекстах, где средство проверки типов не может прийти к выводу об этом факте. В частности, операция x!создает значение типа xwith nullи undefinedисключено. Подобно утверждениям типа форм <T>xи x as T, !оператор ненулевого утверждения просто удаляется в выдаваемом коде JavaScript.

Я нахожу использование термина «утверждать» немного вводящим в заблуждение в этом объяснении. Это «утверждение» в том смысле, что разработчик утверждает это , а не в том смысле, что тест будет выполняться. Последняя строка действительно указывает, что это приводит к тому, что код JavaScript не генерируется.


102
Хороший призыв к «утверждать» двусмысленность.
Estus Колба

8
Хорошее объяснение. Я считаю хорошей практикой делать с console.assert()соответствующей переменной перед добавлением !после нее. Поскольку add !говорит компилятору игнорировать проверку на ноль, он компилируется в noop в javascript. Так что, если вы не уверены, что переменная не равна нулю, тогда лучше сделать явную проверку утверждения.
Jayesh

12
В качестве мотивирующего примера: использование нового типа карты ES с кодом, подобным dict.has(key) ? dict.get(key) : 'default';компилятору TS, не может сделать вывод, что getвызов никогда не возвращает null / undefined. dict.has(key) ? dict.get(key)! : 'default';сужает тип правильно.
kitsu.eb

1
Есть ли сленг для этого оператора, например, как оператор Элвиса относится к бинарному оператору?
ебакунин

@Jayesh, не могли бы вы рассказать о хорошей практике console.assert (), не могли бы вы опубликовать пример?
Кристофер Франциско

169

Ответ Луи великолепен, но я подумал, что я хотел бы кратко изложить его:

Оператор bang указывает компилятору временно ослабить ограничение «не ноль», которое может потребоваться в противном случае. Он говорит компилятору: «Как разработчик, я лучше вас знаю, что эта переменная не может быть нулевой прямо сейчас».


85
Тогда, как разработчик, вы все испортили.
Майк Чемберлен

9
Или, как компилятор, он испортил. Если конструктор не инициализирует свойство, но ловушка жизненного цикла делает это, и компилятор не распознает это.
Мукус

26
Это не ответственность компилятора TS. В отличие от некоторых других языков (например, C #), JS (и, следовательно, TS) не требует инициализации переменных перед использованием. Или, если посмотреть по-другому, в JS все переменные объявлены varили letнеявно инициализированы undefined. Кроме того, свойства экземпляра класса могут быть объявлены как таковые, что class C { constructor() { this.myVar = undefined; } }вполне допустимо. Наконец, хуки жизненного цикла зависят от структуры; например, Angular и React реализуют их по-разному. Поэтому нельзя ожидать, что компилятор TS расскажет о них.
Майк Чемберлен

1
Существует ли действительный вариант использования для оператора взрыва, учитывая удивительность анализа типов на основе потока управления в TS?
Евгений Каратаев

1
@EugeneKarataev Да, фреймворки часто инициализируют переменные внутри себя, и анализ потока управления ts не может их уловить. Его использование, конечно, уменьшено, но вы встретите случаи, когда вам это нужно.
arg20
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.