Поля класса или локальные переменные, которые по сценарию могут иметь значение undefined, способны обрушить ожидаемый ход программы. Неудивительно, что TypeScript реализует механизмы, защищающие от подобных случаев. Но, кроме того, он также реализует механизмы, позволяющие обходить предыдущие, когда поведение лишь похоже на нежелательное по архитектурным причинам. Если кажется непонятным, то не беда, ведь данная глава и призвана помочь в этом разобраться.
Модификатор утверждения непринадлежности значения к типу undefined¶
Для повышения типобезопасности программы, рекомендуется вести разработку с активной опцией --strict (глава Опции компилятора), активирующей множество других опций, изменяющих поведение компилятора и тем самым заставляя разработчиков писать код, сводящий к минимуму ошибки на этапе выполнения.
Это привело к созданию опции --strictPropertyInitialization, которая при активной опции --strictNullChecks запрещает классу иметь поля, типы которых явно не принадлежат к undefined и которые не были инициализированы в момент его создания. Таким образом, предотвращается обращение к полям, которые могут иметь значение undefined.
1 2 3 4 5 6 7 8 910
classIdentifier{publica:number=0;// Ok, инициализация при объявленииpublicb:number;// Ok, инициализация в конструктореpublicc:number|undefined;// Ok, явное указание принадлежности к типу Undefinedpublicd:number;// Error, инициализация отсутствуетconstructor(){this.b=0;}}
Но бывают случаи, при которых условия, устанавливаемые опцией --strictPropertyInitialization, не могут быть удовлетворены в полной мере. К самым распространенным случаям можно отнести установку значений полей с помощью DI (dependency injection), инициализации, вынесенной в методы жизненного цикла (life cycle), а также методы инициализации, выполняемые из конструктора класса.
12345
// инициализация с помощью DIclassA{@Inject(Symbol.for('key'))publicfield:number;// Error}
12345678
// метод жизненного цикла из AngularclassB{privatefield:number;// ErrorpublicngOnInit():void{this.field=0;}}
1 2 3 4 5 6 7 8 9101112
// инициализация вне конструктораclassC{privatefield:number;// Errorconstructor(){this.init();}privateinit():void{this.field=0;}}
Для таких случаев синтаксис TypeScript содержит модификатор definite assignment assertion modifier, который указывается с помощью символа восклицательного знака (!), располагаемого после идентификатора поля или переменной.
Применяя модификатор definite assignment assertion modifier, разработчик сообщает компилятору, что берет ответственность за инициализацию поля на себя.
12345
// инициализация с помощью DIclassA{@Inject(Symbol.for('key'))publicfield!:number;// Ok}
12345678
// метод жизненного цикла из AngularclassB{privatefield!:number;// OkpublicngOnInit():void{this.field=0;}}
1 2 3 4 5 6 7 8 9101112
// инициализация вне конструктораclassC{privatefield!:number;// Okconstructor(){this.init();}privateinit():void{this.field=0;}}
Данный модификатор можно применять только тогда, когда доподлинно известно, что поле или переменная будет инициализирована каким-либо механизмом, выполняемым во время выполнения. В остальных случаях данный механизм лишь сведет на нет работу компилятора TypeScript.