javascript

Снова any. Заметка для новичка

  • суббота, 14 сентября 2024 г. в 00:00:05
https://habr.com/ru/articles/843116/

Поводом для данной заметки стали несколько обстоятельств. Негативный опыт на одном проекте, и следующий спич в одном из докладов по ТС 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:

  1. Задание формы для типа параметра декорируемого подсчитывающей функцией

  2. Отключение типизации, т.к. в данном случае нас вообще не интересует с какими параметрами работает декорирующая функция. О количестве параметров должна заботится декорируемая функция.

Без 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 просто так, то точно имеет смысл ознакомиться с особенностями из этой заметки.