Тип any
На таможне бывают посылки со штампом «не досматривать». Инспектор не открывает такую коробку и не сверяет её с декларацией: что бы ни лежало внутри, посылка проходит границу. Это удобно, но если внутри не то, что ждали, выяснится это уже в стране — когда коробку откроют.
В TypeScript такой штамп называется any. Сравним две программы. В первой у переменной честная декларация:
const amount: number = 42;
console.log(amount.toUpperCase()); // ✗error TS2339: Property 'toUpperCase' does not exist on type 'number'.tsc видит, что у числа нет метода toUpperCase, и сообщает об ошибке до запуска. Теперь поставим на ту же посылку штамп:
const amount: any = 42;
console.log(amount.toUpperCase()); // ✗TypeError: amount.toUpperCase is not a functiontsc молчит, а программа падает при запуске. Ошибка никуда не делась: она переехала из проверки в ту минуту, когда программа уже работает у пользователя.
any выключает проверку
Тип any значит «здесь может быть что угодно, не проверяй». Для такого значения компилятор разрешает всё:
- вызвать любой метод:
amount.toUpperCase(); - прочитать любое свойство, даже несуществующее:
amount.size; - положить значение в переменную любого типа:
const text: string = amount; - присвоить ему что угодно: число, строку, объект.
Остальной код программы tsc проверяет как обычно. Штамп стоит только на этой посылке.
Откуда берётся any
Строгий режим ловит только неявный any: как вы помните из прошлой темы, параметр без типа tsc не пропускает. А any, написанный явно или полученный из чужого кода, строгий режим не останавливает.
Чаще всего any пишете не вы. Функция JSON.parse возвращает any: она не знает, что окажется в строке. Наведите курсор на data в редакторе, и подсказка покажет const data: any.
const data = JSON.parse('{"title": "report"}');
const label = data.title.toUpperCase();
const size: number = label;
console.log(size); // REPORTРазберём пример по строкам:
dataполучает типanyотJSON.parse.data.title— тожеany, и результатtoUpperCase()сноваany. Поэтому типlabel—any.sizeобъявлена какnumber, ноtscразрешает положить в неёlabel: значениеanyподходит к любому типу.- Программа печатает
REPORT— строку из переменной, которая по декларации хранит число.
any расползается по программе
Пример показывает главную опасность any: он заражает соседей. Свойство, прочитанное у значения any, и результат вызова его метода — тоже any. Одно значение из JSON.parse незаметно выключило проверку у label, а через неё обмануло переменную size.
Здесь таможенная аналогия перестаёт работать. В жизни штамп стоит на одной коробке, а в TypeScript он переходит на всё, что из этой коробки достали.
На собеседовании часто спрашивают, чем опасен any. Хороший ответ: any выключает проверку для значения и всего, что из него получено, поэтому ошибки типов превращаются в ошибки при запуске.
Частая ошибка
Параметру написали any, чтобы tsc перестал сообщать об ошибке в вызове:
function double(x: any) { // ✗
return x * 2;
}
console.log(double("ten"));tsc молчит, а программа печатает NaN: строку "ten" нельзя умножить на 2. С честным типом x: number компилятор поймал бы вызов double("ten") ещё до запуска:
error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.Когда tsc сообщает об ошибке, исправляйте тип или вызов, а не выключайте проверку.
Главное
anyвыключает проверку:tscразрешает с таким значением любые методы, свойства и присваивания.- Ошибка с
anyне исчезает, а переезжает в момент запуска. JSON.parseвозвращаетany, и всё, что прочитано уany, — тожеany.- Значение
anyподходит к переменной любого типа, поэтомуanyобманывает даже переменную типаnumber.