Поводом для данной заметки стали несколько обстоятельств. Негативный опыт на одном проекте, и следующий спич в одном из докладов по ТС 2023 года:
 "Так когда же использовать any? Никогда. Шучу, конечно. Если идет портирование или при разработке дженериков можно" - за точность уже отвечать не могу, но смысл примерно такой.
 А так же заявления некоторых команд в духе: "У нас отличный проект. У нас нет any"
Так как относиться неискушенному разработчику к any?
Документация
Первым делом обратимся к современной документации на ТС. А имеем мы следующие:
TypeScript also has a special type, any, that you can use whenever you don’t want a particular value to cause typechecking errors.
И подобное действительно может ввести в заблуждение, что с any можно обращаться как с любым другим типом и использовать при разработке.
 Потому что звучит это как: "У нас тут тип, что бы скрыть сообщения об ошибках типизации"
Пример 1.
export const anyAgainEx1 = () => {
  const A: any = 1
  const B: string = A
  const C = B.repeat(10)
}
Запустив тест мы получим подтверждение, что функция выкинет исключение с ошибкой B.repeat is not a function
Таким образом, использовать any как тип в ТС проекте нельзя, потому что основная его функция - это отключать типизацию в месте использования.
И в документации об этом написано прямым текстом. Но не в самом разделе посвященном any, а в, на мой взгляд весьма отдаленном разделе, Do's and Don'ts:
Don’t use any as a type unless you are in the process of migrating a JavaScript project to TypeScript. The compiler effectively treats any as “please turn off type checking for this thing”. It is similar to putting an @ts-ignore comment around every usage of the variable. This can be very helpful when you are first migrating a JavaScript project to TypeScript as you can set the type for stuff you haven’t migrated yet as any, but in a full TypeScript project you are disabling type checking for any parts of your program that use it.
И если мы не знаем какой тип должен быть на месте должны использовать unknown
Пример 2.
export const anyAgainEx2 = () => {
  const A: unknown = 1
  const B: string = typeof A === 'string' ? A : '1'
  return B.repeat(2)
}
Generic
Когда у нас есть ТС проект any в нем все равно использовать можно.
Пример 3.
type A<T> = { value: T }
type B<T> = T extends any ? A<T> : never
type C<T extends { value: any }> = T extends { value: infer InnerT } ? InnerT : never
type testType = string | number
type Result = {
value1: A<testType> // { value: string | number }
value2: B<testType> // { value: string } | { value: number }
value3: C<A<string> | B<number>> // string | number
}
Мы имеем два примера выразительного использования any
- В первом случае таким образом ТС позволяет включать дистрибутивность объединения при передаче в дженерик 
- Во втором случае с помощью - anyмы определили форму ограничения для типа дженерика
Отключение типизации через any как рабочий вариант
Задача: Написать декоратор для функции, который подсчитывает количество вызовов
Пример 4.
export const anyAgainCounts: { [key: string]: number } = {}
const decoratorCount = function<T extends (...p: any) => any>(fn: T, desc: string): T {
  anyAgainCounts[desc] = 0
  return ((...params: any[]) => {
    anyAgainCounts[desc]++
    return fn(...params)
  }) as T
}
Ключевые моменты использования any:
- Задание формы для типа параметра декорируемого подсчитывающей функцией 
- Отключение типизации, т.к. в данном случае нас вообще не интересует с какими параметрами работает декорирующая функция. О количестве параметров должна заботится декорируемая функция. 
Без any пример выглядит так:
const decoratorCount2 = <F extends (...args: Parameters<F>) => ReturnType<F>>(fn: F, desc: string) => {
  anyAgainCounts[desc] = 0
  return ((...params: Parameters<F>) => {
    anyAgainCounts[desc]++
    return fn(...params)
  })
}
Ключевой момент: дизайн системы типов усложнился, но при этом в самой реализации мы ничем из этого не пользуемся.
Заключение
Так что же означает фраза: "У нас на проекте нет any"?
Во-первых, это говорит о стадии проекта. Либо он изначально был на ТС, либо все операции портирования завершены.
Во-вторых, any до сих пор может эффективно использоваться в TC проекте, но вот как тип его использование ограничено ясными продуманными ситуациями. Если же в проекте и в самом деле недолюбливают any просто так, то точно имеет смысл ознакомиться с особенностями из этой заметки.
 
           
 
meonsou
А чего такого выразительного здесь в
any? Сами же пишете чтоПочему здесь
anyстал лучше (или не лучше?) чемunknown? Ну и в примере с дистрибутивностью тоже можно на него заменить например.А вот так уже лучше не делать.
Во первых, ограничив
fn: Tчерез(...p: any) => any, мы получаем возможность использовать его в функции как если бы он имел этот тип. То есть например можно вызывать переданную нам функциюfnс рандомными аргументами, потому что проверки отключены. Тут потенциал для стрельбы по ногам бесконечный.Во вторых, мы потеряли важный документирующий аспект типов. Когда человек видит что аргументы одной функции определены через аргументы другой функции например, он понимает что тут есть какая-то логическая связь. Не говоря уже о компиляторе.
Эту функцию можно типизировать без
anyпроще:Здесь решены все перечисленные недостатки + корректно обрабатываются дженерики в
fn.Приведу примеры немного более ясных и продуманных ситуаций.
Всё дело обычно во вариантности. При написании ограничений часто нужен самый общий тип, и если этот тип параметризован то в ковариантных позициях ставится
unknown(или максимально допустимый тип), а в контравариантныхnever. В этих случаях можно обходиться безany, но это может быть затруднено 1) слишком сложными ограничениями (много букв) 2) сложностью определения вариантности. Ну а если параметр инвариантный то выбора нету совсем.Пример:
Тут стоит иметь в виду что можно например применить
B<(arg: 1) => 2>, при том что тип(arg: 1) => 2на самом деле невозможно получить с помощьюAи значение такого типа невозможно присвоить вA<_>, за исключениемA<any>. Поэтому при расстановкеanyв ограничения тоже следует быть осторожным.Ещё из интересных применений можно выделить например тайпгарды. Попробуем проверить что тип является тайпгардом, для простоты ограничимся одним параметром.
Тайпгард имеет вид:
То есть мы не можем просто так обобщить его через
TypeGuard<never, unknown>, так какRдолжен быть не ширеA. Приходится использоватьany.Это же будет актуально для других типов с подобными ограничениями.
Vitaly_js Автор
то, что я написал относится к использованию any как тип на месте. К дженерикам это не относится. Там any работает как супер-тип, поэтому любой тип может его расширять. Т.е. речь идет об отношении типов, а не типе на месте. В дженериках не нужно делать никакого приведения к типу.
А второй момент, использование any для дистрибутивности объединений - это официальный подход описанный в документации.
Основной "косяк" any при использовании в дженериках не работает. Поэтому, думаю, его так "легко" и используют в дженериках и документации.
нет, не получим. Потому что "any" мы приводим к T. Поэтому, получается совершенно безопасная реализация не загроможденная всякими служебными типами, которые нам все равно не нужны. И ваши примеры будут выдавать ошибки.
давайте посмотрим, что видит разработчик.
Я использовал распространенный шаблон для описания типа функции с любыми параметрами и любым возвращаемым значением. И этот шаблон использован на месте параметра. Семантически все верно. Любая функция может быть парметром декоратора. Поэтому все что я хотел показать отражено в типе. А связь о которой вы говорите должна быть отражена в имени декоратора.
вот как раз, что бы такой ерундой не страдать `function decoratorCount<A extends unknown[], R>(fn: (...args: A) => R, desc: string)` я и использовал распространенный тип функции в смысле описанном выше как ограничение, и T на месте параметра. Мой пример легко читается и легко понимается. А вот этот пример, на мой взгляд, очень невыразительный. Плюс, оставляет место для дискуссии, почему не использовать пример из моей статьи вместо вашего (там где нет any)? Что бы все это убрать, я и использовал any
На часть комментария ниже я ответить не смогу. Я вообще не понял, что вы хотите сказать.
Сам я выбирал примеры, которые с одной стороны распространены, а с другой используются в документации. Поэтому мои примеры легко использовать в проекте. Тот же декоратор можно использовать для каких-нибудь дебоунсов и тротлингов. Ваши примеры я раскусить не смог. Сам я такие штуки еще ни разу не видел, но с документацией ознакомился: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#optional-variance-annotations-for-type-parameters, поэтому спасибо узнал что-то новое. Но, если есть возможность я бы хотел увидеть, что-то менее синтетическое.
meonsou
Не понял что за "тип на месте". Вы пишете что
T extends { value: any }это пример "выразительного использования"any. Я просто поинтересовался чем именно он выразителен в сравнении, например, сunknown. Ни о каком приведении речи не шло.Ну вот же, вы бы проверили хоть.
Ну и почему она не должна быть отражена в типе? Я тоже могу заявить что
const x: numberписать излишне, можно же всё отразить в имениconst number_x: any.А ещё легко ломается, см. плейграунд выше. Мало того что
anyвнутри функции, так ещё иas.Потому что он проще и букв меньше
Потому что он, например, сохраняет дженерики в переданной функции (я писал об этом)
Ну и в целом странная аргументация. А места для дискуссии почему
anyвместо нормальных типов не остаётся значит?Я привёл примеры где
anyдействительно может быть нужен и его проблематично/невозможно заменить.Примеры не слишком распространённые как раз потому что ситуация где действительно необходим
anyне распространённая. Примеры либо синтетические, либо километровые, но в любом случае достаточно сложные. В простых случаях уровняT extends { value: any }я не вижу смысла использоватьanyвместоnever/unknown, потому что 1) это и так ничего не стоит 2)anyвсё ещё может попасть по ногам. Лучше отучаться от его использования и знать где он действительно нужен (такое бывает редко).