Главная / Статьи

Обновление кодбазы React до TypeScript 6.0: что ломается в первую очередь

Как более строгие стандартные значения в TypeScript 6.0, исправление инференции методов, импорты по подпутям #/ и типы Temporal влияют на React-приложение, а также чек-лист для безопасного обновления.

Издатель: REACTAPP.TOP4 мин чтения842 слов

Источники: TypeScript 6.0 for React Developers: What Actually Changed

Большинство обновлений компилятора проходят незамеченными. TypeScript 6.0 отличается: это последняя версия, созданная на основе оригинального компилятора JavaScript, и она в основном предназначена для подготовки кодовых баз к TypeScript 7, основанному на Go. Поэтому в ней меньше новых функций, а больше изменений, которые выявляют существующие проблемы. В этом руководстве описывается, как именно изменится работа с кодовыми базами на React, и в конце приводится чек-лист для обновления. Чтобы узнать, какое место занимает 6.0 в процессе перехода на нативный компилятор, ознакомьтесь с роль TypeScript 6 в качестве моста на пути к нативному компилятору TS 7.

Сначала более строгие настройки по умолчанию

В TypeScript 6.0 режим строгости включен по умолчанию, предполагается использование ES-модулей, а целевая версия по умолчанию изменена на es2025. Проекты, в которых каждый параметр задан явно, не пострадают; проекты, зависевшие от старых более либеральных настроек по умолчанию, столкнутся с массой новых ошибок. В коде React обычно страдают более старые компоненты: свойства с нестрогой типизацией, обработчики с имплицитными параметрами типа any, ссылки, которые могут иметь значение null, и функция useState() без аргумента типа. Это не регрессии, а давно существовавшие неоднозначности, которые теперь фиксируются.

Цели es3 и es5 также считаются устаревшими, поэтому минимальным требованием становится es2015. Для многих приложений на React это почти не меняет ситуацию, поскольку Babel, SWC или esbuild занимаются приведением синтаксиса к более старым версиям, а tsc лишь проверяет типы, но перед тем, как изменять поддерживаемые браузеры, убедитесь в этом. Выполните обновление на отдельной ветке с полным набором файлов и тестов; изменение настроек компилятора в пятницу днем — надежный способ потерять выходные.

Однородное применение правил к функциям в стиле методов

Более тонкая проблема связана с ситуацией, когда функция считается «чувствительной к контексту», то есть типы её параметров должны быть определены из контекста, прежде чем можно будет продолжить процесс вывода типов. В версии 6.0 выражение function или сокращённое обозначение метода рассматривается таким образом только тогда, когда оно действительно использует this. Раньше выбор синтаксиса метода вместо функции-стрелки мог изменить результаты вывода типов, даже если this вообще не участвовал в операциях. Приведённый ниже пример объединяет оба стиля:

// Before: inconsistent inference depending on syntax
const useCounter = (initial) => {
  const [count, setCount] = useState(initial);

  // this pattern used to infer differently than an arrow fn
  const increment = function () {
    setCount((c) => c + 1);
  };

  return { count, increment };
};

Ранее кэшбек, такой как increment, мог выводиться с другими типами по сравнению с эквивалентной функцией-стрелкой, и в более крупных примерах такая разница могла привести к тому, что слабый тип попадал к вызывающему коду. Теперь синтаксис, который вы выбираете для кэшбеков и возвращаемых вспомогательных функций, больше не влияет на получаемые типы, что устраняет причину путаницы, связанную с вопросами вроде «почему этот кэшбек не проверяется?».

Более чистые импорты с префиксом #/

Импорты подпутей в Node определяют приватные псевдонимы в поле imports файла package.json, и TypeScript 6.0 поддерживает префикс #/ в этом поле, соответствуя псевдонимам корня, которые часто используются бандлерами. Объявляйте сопоставление один раз:

{
  "imports": {
    "#/*": "./src/*"
  }
}

Затем компоненты импортируют элементы из корня проекта, вместо того чтобы использовать сегменты ../:

// No more relative path spaghetti in deeply nested components
import { useAuth } from "#/hooks/useAuth";
import { Button } from "#/components/ui/Button";

Поскольку псевдоним хранится в package.json, а не только в разделе paths файла tsconfig.json, Node и TypeScript обрабатывают его одинаково, без необходимости использования дополнительных плагинов. Это работает только в режимах разрешения, которые читают импорты из package.json, таких как nodenext или bundler.

Временные типы поставляются встроенными

В TypeScript 6.0 добавлены декларации для API Temporal, который достиг заключительной стадии стандартизации в рамках TC39. Команды, разрабатывающие интерфейсы для планирования, бронирования или дашбордов, могут использовать Temporal.PlainDate или Temporal.ZonedDateTime без необходимости подключения дополнительных пакетов, вместо того чтобы иметь дело с типом Date. Однако эти типы не обеспечивают поддержки во время выполнения кода: проверьте нативную поддержку в выбранных браузерах и версиях Node и добавьте полифил, если это необходимо.

Стоит ли обновляться прямо сейчас?

Да, целенаправленно. Команда TypeScript представляет версию 6.0 как стабильное обновление, но изменения внутри действительно существуют:

  • Проведите аудит файла tsconfig.json и закрепите все параметры, от которых вы зависите.
  • Исправляйте новые ошибки строгого режима в старых компонентах, а не маскируйте их с помощью типа any.
  • Уточните, какой инструмент осуществляет обратную совместимость и какая версия целевой среды ему требуется.
  • Для кодовых баз с компонентами классов или хранилищами, управляемыми классами, рассмотрите возможность включения параметра noImplicitOverride, чтобы для переопределенных членов требовалось использование ключевого слова override.
  • Заключение

    TypeScript 6.0 не изменит способ написания кода на React, но выявит проблемы, скрытые ранее из-за слабых стандартных настроек. Решение этих проблем сейчас, в удобное для вас время, значительно дешевле, чем делать это позже в году при переходе с версии 6 на 7, когда это станет в основном вопросом производительности.