TS7006: параметр неявно имеет тип any

базовые типы≈ 8 минут

Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.

Компилятор выводит тип почти отовсюду: из значения, из литерала, из соседней строки. У параметра функции выводить не из чего — значение туда придёт только в момент вызова. Тогда он останавливается и пишет TS7006: это не придирка к стилю, а сообщение о том, где именно проверки закончились.

Разделы темы:·

В TypeScript типы почти нигде не пишут руками: компилятор смотрит на значение и делает вывод сам. Проблема начинается там, где значения ещё нет.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Диагностика приходит на вторую строку: Параметр "w" неявно имеет тип "any". Ключевое слово здесь — «неявно»: any в коде никто не писал, компилятор подставил его сам, потому что других вариантов у него не было. Проверка noImplicitAny, которая входит в strict, запрещает делать это молча.

Обратите внимание, что вызов area(width) стоит строкой ниже и аргумент там очевидно число — а тип всё равно не вывелся. Компилятор смотрит на объявление функции и только на него.

Соблазн понятный: вызов один, аргумент числовой, бери и выводи. Но сигнатура функции — это её договор с остальной программой, а не итог наблюдения за сегодняшними вызовами. Функцию позовут из другого файла, из колбэка, через год. Если бы её тип пересобирался по всем найденным вызовам, добавление одной новой строки где-то в углу проекта меняло бы тип функции и роняло проверки в местах, которых никто не трогал.

Вывод «по месту вызова» в TypeScript всё-таки есть — но он выводит не сигнатуру, а подстановку в дженерик, и делает это отдельно для каждого вызова. До дженериков дойдём в седьмой главе.

Отмахнуться от TS7006 легко: код же работает. Беда в том, что any не остаётся в той строке, где появился.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Диагностика ровно одна и указывает на первую строку. Ломается — последняя. Для компилятора обе последние строки одинаково законны: first вернула any, константа name стала any, а у значения типа any можно вызвать что угодно. Разницу нашёл уже движок.

TS7006 сообщает не «вы не дописали тип», а «с этого места я перестал проверять».

Второй пример короче, зато нагляднее: тут падение случается прямо в той же функции, где живёт неявный any.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Компилятор ругнулся на объявление и промолчал про вызов с числом — параметру типа any можно передать что угодно. И заметьте: диагностика не отменила выполнение. Так же ведёт себя tsc по умолчанию — он выдаёт JavaScript даже при ошибках типов, потому что типы живут только на этапе сборки.

Лечится одним двоеточием. Тот же shout, отличие в одном слове.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Главное здесь — куда переехала диагностика: с объявления на вызов, на пятую строку. Это и есть смысл аннотации. Было «я не знаю, что тут», стало «сюда нельзя передавать число» — и сказано это ровно в том месте, где написана ошибка.

Аннотировать нужно только вход. Что функция возвращает, компилятор посчитает сам.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Метод toFixed в последней строке разрешён именно потому, что вывод возврата сработал: size — это number, а не any. Возвращаемый тип пишут отдельно, когда его хотят зафиксировать как договор, но это тема главы «Функции в типах».

Есть и второй способ дать компилятору значение — значение по умолчанию. Оно работает как аннотация, хотя двоеточия в коде нет.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Если у параметра есть значение по умолчанию, TS7006 не будет: компилятору хватило и такого значения.

Если бы каждый параметр требовал аннотации, писать на TypeScript было бы невыносимо: колбэков в коде больше, чем именованных функций. Но у колбэка есть то, чего нет у обычной функции, — известное заранее место, куда его кладут.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Такой вывод называют контекстной типизацией: тип берётся не из значения, а из контекста — из того, чем обещали быть функцию. Работает он ровно там, где контекст есть. Скопируем ту же стрелку в константу.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Функция буквально та же, а диагностика вернулась. В момент объявления она лежит в обычной константе, и никто не обещал, что её отдадут в map массива чисел, — может, её вообще не вызовут. Это самое частое «я же ничего не менял» в TypeScript: вынесли колбэк из вызова, чтобы не мешался, и он тут же потерял типы.

Способа два, и они уже знакомы: аннотация на параметре — либо тип на самой константе, который и станет контекстом.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Второй вариант ценнее, чем кажется: аннотация описывает один параметр, а тип константы — всю функцию целиком, вместе с возвратом. Если завтра стрелка начнёт возвращать строку, компилятор скажет об этом сразу, а не там, где результат пытались сложить.

TS7006 — самый частый, но не единственный код из этого семейства. Соседи выглядят иначе, а причина одна.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Здесь код другой — TS7031, «Элемент привязки ... имеет неявный тип "any"», и диагностик сразу две: имена из деструктуризации компилятор считает по отдельности. Аннотация вешается не на каждое имя, а на связку целиком: function show({ name, age }: { name: string; age: number }).

Пустой массив — отдельный сюжет. Пока переменную заполняют рядом, компилятор следит за присваиваниями и уточняет тип по ходу; этот механизм называют «развивающимся any». Но следить он умеет только в пределах одного потока выполнения.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Две диагностики из-за одного не написанного типа: TS7034 на объявление и TS7005 на чтение. Лечение прежнее — сказать вслух: let items: string[] = [].

И третий случай, знакомый по части I: this в обычной функции.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Код диагностики — TS2683, и здесь компилятор оказался прав дважды. Вывод TypeScript строгий, а значит, в готовом коде появляется "use strict", и this при обычном вызове равен undefined — ровно то, о чём была глава про контекст. Компилятор предупредил, предупреждение проигнорировали, движок уронил.

КодГде вылезаетЧто говорит компилятор
TS7006параметр функцииПараметр "w" неявно имеет тип "any".
TS7031имя из деструктуризации в параметрахЭлемент привязки "name" имеет неявный тип "any".
TS7034объявление переменной без типа и без внятного значенияПеременная "items" неявным образом получает тип "any[]" в некоторых местах, где ее тип невозможно определить.
TS7005чтение такой переменнойПеременная "items" неявно имеет тип "any[]".
TS2683this в обычной функцииthis неявно содержит тип any, так как он не имеет аннотации типа.

Первое, что находится по запросу «TS7006», — совет поставить "noImplicitAny": false. Совет работает ровно так, как обещано, и стоит дороже, чем выглядит.

Флаг не точечный: он гасит проверку во всём проекте, а не в той строке, где вы торопитесь. Каждый следующий забытый параметр молча станет any — и потащит за собой всё, что из него достанут, как в примере с first. Правило само по себе входит в strict, так что выключение — это осознанное «строго, но вот эту проверку не надо».

Главное же: TS7006 не запрещает any. Он запрещает any молча. Напишите его руками — и компилятор замолчит без всяких флагов.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

От того же shout без аннотации этот код отличается одним словом, и поведение у них одинаковое — падение то же самое. Разница в другом: any теперь виден. Его найдёт поиск по проекту, на него настроено правило линтера, его заметят на ревью. Неявный any не видит никто.

А если тип и правда заранее неизвестен, есть дверь получше — unknown: он тоже принимает что угодно, но требует разобраться, прежде чем что-то делать.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Чем unknown отличается от any и почему выбирать почти всегда стоит его — в следующей статье главы.

  • TS7006 появляется там, где выводить не из чего: у параметра нет значения до вызова, а на вызовы компилятор не смотрит.
  • Неявный any — не про стиль: он гасит проверки не в одной строке, а во всём, что из этого значения получилось.
  • Лечится аннотацией параметра, значением по умолчанию или типом на самой функции; возврат компилятор выведет сам.
  • У колбэка внутри вызова тип берётся из контекста — но стоит вынести его в отдельную константу, и TS7006 возвращается.
  • Рядом живут TS7031 (деструктуризация), TS7034 и TS7005 (переменная без типа), TS2683 (this) — причина у них общая.
  • Выключать noImplicitAny нечестно: он запрещает не any, а молчаливый any. Нужен любой тип — пишите any руками, а лучше unknown.

1/4Функция area(w) объявлена без аннотации, а строкой ниже вызвана как area(100). Почему компилятор не вывел для w тип number?

2/4Стрелка (price) => price * 1.2 прямо внутри prices.map(...) не вызывает TS7006, а она же, вынесенная в отдельную константу, — вызывает. В чём разница?

3/4Параметр неявно имеет тип any, внутри функции у него вызывают toUpperCase(), а саму функцию зовут с числом. Что произойдёт в песочнице?

4/4Что на самом деле делает "noImplicitAny": false?

Убрать три TS7006

Функция работает, но компилятор выдаёт три диагностики подряд. Расставьте аннотации так, чтобы вывод остался прежним, а диагностики исчезли.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Каждый параметр получает свою аннотацию; тип возврата дописывать не нужно — компилятор выведет string сам.

tsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Проверить, что аннотации работают, проще всего вызовом с неправильным аргументом: describe(30, "Рико", true) теперь даст TS2345 на строке вызова, а раньше прошёл бы молча.

Колбэк, потерявший типы

Пока перебор был написан одной строкой, диагностик не было. Колбэк вынесли в отдельную функцию — появился TS7006. Верните типы, не возвращая код обратно в вызов.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Вне вызова контекста нет, поэтому тип нужно назвать самому.

tsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Если колбэк — стрелка в константе, аккуратнее описать её целиком отдельным типом: тогда проверится и возврат, а не только вход.

tsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Пустой массив

Здесь диагностик две и коды разные — TS7034 на объявление и TS7005 на чтение. Почините одной правкой и объясните себе, почему без функции add тот же код был бы чистым.

ts
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.
страница примера

Правка одна — аннотация на объявлении.

tsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Без функции — let items = []; items.push("первый"); — компилятор молчит: он видит весь путь значения подряд и уточняет тип по присваиваниям, это «развивающийся any». Как только заполнение уезжает в отдельную функцию, порядок вызовов ему неизвестен, следить больше не за чем — и он требует сказать тип вслух.