По умолчанию null
и undefined
можно назначать всем типам в TypeScript, например:
let foo: number = 123;
foo = null; // Okay
foo = undefined; // Okay
Это спроектировано по образцу того, как многие люди пишут JavaScript. Однако, как и в других случаях, TypeScript позволяет вам явно указать, чему можно, а чему нельзя присвоить null
или undefined
.
В режиме strictNullChecks
, значения null
и undefined
различаются:
let foo = undefined;
foo = null; // не Okay
Допустим, у нас есть интерфейс Member
:
interface Member {
name: string,
age?: number
}
Не каждый Участник
предоставит свой возраст, поэтому возраст
является необязательным свойством, то есть значение age
может быть или не быть undefined
.
undefined
- корень всех зол. Это часто приводит к ошибкам во время выполнения. Легко написать код, который будет выдавать Error
во время выполнения:
getMember()
.then(member: Member => {
const stringifyAge = member.age.toString() // Невозможно прочитать свойство toString из undefined
})
Но в режиме strictNullChecks
эта ошибка будет обнаружена во время компиляции:
getMember()
.then(member: Member => {
const stringifyAge = member.age.toString() // Возможно, объект 'undefined'
})
Новый постфиксный оператор !
может использоваться для утверждения, что его операнд не равен null и не-undefined в случаях, где средство проверки типов не может сделать логический вывод об этом. Например:
// Скомпилировано с --strictNullChecks
function validateEntity(e?: Entity) {
// Выбрасываем исключение, если e - null или невалидный объект
}
function processEntity(e?: Entity) {
validateEntity(e);
let a = e.name; // ОШИБКА TS: e может быть null.
let b = e!.name; // OKAY. Мы утверждаем, что e не null.
}
Обратите внимание, что это просто утверждение, и, как при утверждении типа, вы сами несете ответственность за то, чтобы значение не было null. Не-null утверждение, по сути, означает, что вы говорите компилятору: "Я знаю, что оно не null, поэтому позволь мне использовать его, как если бы оно не было null".
TypeScript также будет жаловаться на свойства в классах, которые не инициализированы, например:
class C {
foo: number; // OKAY, так как назначено в конструкторе
bar: string = "hello"; // OKAY, так как есть инициализатор свойства
baz: boolean; // ОШИБКА TS: свойство 'baz' не имеет инициализатора и не назначается напрямую в конструкторе.
constructor() {
this.foo = 42;
}
}
Вы можете использовать утверждение окончательного присваивания с постфиксом к имени свойства, чтобы сообщить TypeScript, что вы инициализируете его где-нибудь, кроме конструктора, например:
class C {
foo!: number;
// ^
// Обратите внимание на этот восклицательный знак!
// Это модификатор "утверждения окончательного присваивания".
constructor() {
this.initialize();
}
initialize() {
this.foo = 0;
}
}
Вы также можете использовать это утверждение с простыми объявлениями переменных, например:
let a: number[]; // Нет утверждения
let b!: number[]; // Утвердить
initialize();
a.push(4); // TS ОШИБКА: переменная использована до присвоения
b.push(4); // OKAY: из-за утверждения
function initialize() {
a = [0, 1, 2, 3];
b = [0, 1, 2, 3];
}
Как и все утверждения, вы говорите компилятору доверять вам. Компилятор не будет жаловаться, даже если код на самом деле не всегда присваивает свойство.