Перейти к содержимому
Теория · Простые и особые типы · урок 3 из 8

Тип any

На таможне бывают посылки со штампом «не досматривать». Инспектор не открывает такую коробку и не сверяет её с декларацией: что бы ни лежало внутри, посылка проходит границу. Это удобно, но если внутри не то, что ждали, выяснится это уже в стране — когда коробку откроют.

В TypeScript такой штамп называется any. Сравним две программы. В первой у переменной честная декларация:

TypeScript
const amount: number = 42;
console.log(amount.toUpperCase()); // ✗
TEXT
error TS2339: Property 'toUpperCase' does not exist on type 'number'.

tsc видит, что у числа нет метода toUpperCase, и сообщает об ошибке до запуска. Теперь поставим на ту же посылку штамп:

TypeScript
const amount: any = 42;
console.log(amount.toUpperCase()); // ✗
TEXT
TypeError: amount.toUpperCase is not a function

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

any выключает проверку

Тип any значит «здесь может быть что угодно, не проверяй». Для такого значения компилятор разрешает всё:

  • вызвать любой метод: amount.toUpperCase();
  • прочитать любое свойство, даже несуществующее: amount.size;
  • положить значение в переменную любого типа: const text: string = amount;
  • присвоить ему что угодно: число, строку, объект.

Остальной код программы tsc проверяет как обычно. Штамп стоит только на этой посылке.

Откуда берётся any

Строгий режим ловит только неявный any: как вы помните из прошлой темы, параметр без типа tsc не пропускает. А any, написанный явно или полученный из чужого кода, строгий режим не останавливает.

Чаще всего any пишете не вы. Функция JSON.parse возвращает any: она не знает, что окажется в строке. Наведите курсор на data в редакторе, и подсказка покажет const data: any.

TypeScript
const data = JSON.parse('{"title": "report"}');
const label = data.title.toUpperCase();
const size: number = label;
console.log(size); // REPORT

Разберём пример по строкам:

  1. data получает тип any от JSON.parse.
  2. data.title — тоже any, и результат toUpperCase() снова any. Поэтому тип label — any.
  3. size объявлена как number, но tsc разрешает положить в неё label: значение any подходит к любому типу.
  4. Программа печатает REPORT — строку из переменной, которая по декларации хранит число.

any расползается по программе

Пример показывает главную опасность any: он заражает соседей. Свойство, прочитанное у значения any, и результат вызова его метода — тоже any. Одно значение из JSON.parse незаметно выключило проверку у label, а через неё обмануло переменную size.

Здесь таможенная аналогия перестаёт работать. В жизни штамп стоит на одной коробке, а в TypeScript он переходит на всё, что из этой коробки достали.

На собеседовании часто спрашивают, чем опасен any. Хороший ответ: any выключает проверку для значения и всего, что из него получено, поэтому ошибки типов превращаются в ошибки при запуске.

Частая ошибка

Параметру написали any, чтобы tsc перестал сообщать об ошибке в вызове:

TypeScript
function double(x: any) { // ✗
    return x * 2;
}
console.log(double("ten"));

tsc молчит, а программа печатает NaN: строку "ten" нельзя умножить на 2. С честным типом x: number компилятор поймал бы вызов double("ten") ещё до запуска:

TEXT
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.