Статический анализ

Статический анализ — это тип тестирования, который обеспечивает автоматическую проверку вашего кода без его фактического запуска или написания автоматического теста. Вероятно, вы уже встречались с такого рода тестированием, если используете такую ​​IDE, как VSCode: проверка типов, выполняемая TypeScript, представляет собой своего рода статический анализ и может отображаться в виде волнистых линий под ошибками или предупреждениями.

ESLint

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

Хорошим примером правила ESLint является правило no-unsafe-finally . Это не позволяет вам писать операторы, которые изменяют поток управления вашей программой внутри finally . Это отличное правило, потому что это необычный способ написания JavaScript, которому может быть сложно следовать. Однако это также то, что должен уметь обнаружить здоровый процесс проверки кода.

  try {
    const result = await complexFetchFromNetwork();
    if (!result.ok) {
      throw new Error("failed to fetch");
    }
  } finally {
    // warning - this will 'overrule' the previous exception!
    return false;
  }

Таким образом, ESLint не является заменой полноценного процесса проверки (и руководства по стилю, определяющего, как должна выглядеть ваша кодовая база), поскольку он не способен охватить каждый неортодоксальный подход, который разработчик может попытаться внедрить в вашу кодовую базу. В руководстве Google по английскому языку есть небольшой раздел о том, как «сохранять простоту».

ESLint позволяет нарушить правило и пометить код как «разрешенный». Например, вы можете разрешить предыдущую логику, аннотировав ее следующим образом:

  finally {
    // eslint-disable-next-line no-unsafe-finally
    return false;
  }

Если вы обнаружите, что постоянно нарушаете правило, подумайте о том, чтобы отключить его. Эти инструменты побуждают вас писать код определенным образом, но ваша команда может привыкнуть к написанию кода другим способом и уже осознавать риски такого подхода.

Наконец, включение инструментов статического анализа в большой базе кода может создать много бесполезного шума (и утомительной работы по рефакторингу) в коде, который в противном случае работал бы нормально. Поэтому легче включить его на ранних этапах жизненного цикла проекта.

Плагины ESLint для поддержки браузера

Вы можете добавить в ESLint плагин, который помечает использование API, которые широко не поддерживаются или не поддерживаются списком целевых браузеров. Пакет eslint-plugin-compat может предупредить вас, когда API может быть недоступен для ваших пользователей, поэтому вам не придется постоянно следить за этим самостоятельно.

Проверка типов для статического анализа

Изучая JavaScript, новые разработчики обычно понимают, что это слабо типизированный язык. То есть можно объявить переменную как одного типа, а затем использовать то же расположение для чего-то совершенно другого. Это похоже на Python и другие языки сценариев, но в отличие от компилируемых языков, таких как C/C++ и Rust.

Этот тип языка может быть хорош для начала работы — и, возможно, именно эта простота сделала JavaScript таким популярным — но он часто становится точкой отказа для некоторых баз кода или, по крайней мере, причиной возникновения запутанных ошибок. Например, при передаче number там, где ожидалась string или тип объекта, это неправильно введенное значение может распространиться по различным библиотекам, прежде чем в конечном итоге вызвать запутанную ошибку TypeError .

Машинопись

TypeScript — наиболее распространенное решение проблемы отсутствия в JavaScript информации о типизации. В этом курсе он широко используется. И хотя это не курс по TypeScript, он может стать важной частью вашего набора инструментов, поскольку обеспечивает статический анализ.

В качестве быстрого примера, этот код, который ожидает получить обратный вызов, принимающий имя string и возраст number :

const callback = (name: string, age: string): void => {
  console.info(name, 'is now', age, 'years old!');
};
onBirthday(callback);

Генерирует следующую ошибку при запуске через TypeScript или даже при наведении указателя мыши в IDE:

bad.ts:4:12 - error TS2345: Argument of type '(name: string, age: string) => void' is not assignable to parameter of type '(name: string, age: number) => void'.
  Types of parameters 'age' and 'age' are incompatible.
    Type 'number' is not assignable to type 'string'.

4 onBirthday(callback);
             ~~~~~~~~

Found 1 error in bad.ts:4
Код из предыдущего примера, отображаемый в IDE, с сообщением об ошибке, отображаемым во всплывающем окне.
VSCode, указывающий, что вы передали неправильный тип.

В конечном счете, цель использования TypeScript — предотвратить появление подобных ошибок (возраст должен быть number , а не string ) в вашем проекте. Ошибки такого рода может быть трудно обнаружить с помощью других типов тестов. Кроме того, система типов может давать обратную связь еще до написания теста. Это может упростить процесс написания кода, предоставляя вам информацию об ошибках типа на ранних стадиях разработки программного обеспечения, а не при его запуске.

Самая сложная часть использования TypeScript — правильная его настройка. Каждому проекту необходим файл tsconfig.json , который, хотя в основном используется самим инструментом командной строки tsc , также читается такими IDE, как VSCode, а также многими другими инструментами и инструментами сборки, включая Vitest. Этот файл содержит сотни опций и флагов, и вы можете найти хорошие ресурсы для его настройки здесь:

Общие советы по TypeScript

При настройке и использовании TypeScript через файл tsconfig.json имейте в виду следующее:

  • Убедитесь, что ваши исходные файлы действительно включены и проверены. Если файл загадочным образом «не содержит ошибок», возможно, это потому, что он не проверяется.
  • Явное описание типов и интерфейсов внутри файлов .d.ts вместо их неявного описания при написании функций может облегчить тестирование вашей кодовой базы. Легче писать макеты и «фальшивые» версии кода, когда задействованные интерфейсы понятны. .

TypeScript неявный любой

Одним из самых мощных и полезных вариантов конфигурации TypeScript является флаг noImplicitAny . Однако зачастую его сложнее всего включить, особенно если у вас уже есть большая база кода. (Флаг noImplicitAny включен по умолчанию, если вы находитесь в strict режиме, но не иначе.)

Этот флаг заставит эту функцию вернуть ошибку:

export function fibonacci(n) {
  if (n <= 1) {
    return 0;
  } else if (n === 2) {
    return 1;
  }
  return fibonacci(n - 1) + fibonacci(n - 2);
}

Хотя читателю совершенно ясно, что n должно быть числом, TypeScript не может это с уверенностью подтвердить. Если вы используете VSCode, при наведении курсора на функцию она будет описана следующим образом:

function fibonacci(n: any): any

Вызывающие эту функцию смогут передавать значение any типа (тип, допускающий любой другой тип), а не только number . Включив флаг noImplicitAny , вы можете защитить этот тип кода во время разработки без необходимости писать обширные тесты бизнес-логики для вашего кода, передающего неправильные типы данных в определенных местах.

Простое решение здесь — пометить аргумент n и тип возвращаемого значения fibonacci как number .

Флаг noImplicitAny не запрещает вам явно писать any в вашей кодовой базе. Вы все равно можете написать функцию, которая принимает или возвращает any тип. Это просто гарантирует, что вы присвоите каждой переменной тип.