TypeScript 7.0 переходит на компилятор Go для достижения максимальной скорости выполнения
TypeScript 7.0 переносит tsc в стек Go с параллельными проверками, новыми значениями по умолчанию для tsconfig, шаблонными литералами Unicode, более строгим анализом JS, а также временным отсутствием программной API.
В течение примерно четырнадцати лет компилятор TypeScript мог самостоятельно собираться. tsc представлял собой версию TypeScript, которая компилировалась в JavaScript и работала под управлением Node. По мере того как объёмы кода достигали миллионов строк, такая схема самостоятельной работы становилась узким местом.
В TypeScript 7.0 изменился язык хостинга. Компилятор и сервис обработки языка были перенесены с TypeScript на Go. Microsoft описывает этот процесс как портирование, а не полную переработку: структура проверки типов осталась прежней, как в версии 6.0, но выполнение происходит с использованием нативного кода и параллелизма на основе общей памяти. По результатам опубликованных сравнений скорость работы превышает скорость TypeScript 6.0 примерно в 10 раз.
Один из часто приводимых примеров нагрузки наглядно демонстрирует эффективность изменений. Проверка структуры проекта в VS Code — объёмом около 1,5 миллиона строк на TypeScript — заняла от примерно 77,8 секунд до около 7,5 секунд.
Если ранее для обозначения этого проекта использовалось кодовое название Project Corsa (а устаревшая версия JavaScript называлась Strada), то это издание представляет собой финальную версию данного проекта.
Почему именно Go, и почему порт?
В общественных дискуссиях часто упоминался Rust. Был выбран Go потому, что его архитектура совпадала с существующим компилятором: сложные графы, механизм сбора мусора и циклические структуры, которые трудно воссоздать с нуля. Именно это совпадение позволило осуществить порт строка за строкой.
Формат порта важнее, чем сам язык. Сохранение архитектуры позволило сохранить существующие правила. Проекты, которые уже проходят проверку типов в версии 6.0 с включенным параметром stableTypeOrdering и без параметра ignoreDeprecations, должны получать одинаковые результаты и в версии 7.0 — это будет более быстрый движок, но не другая система типов.
Сначала проводились тесты на нагрузку в производственных условиях. За более чем год до достижения этой вехи порт работал с огромными объемами данных на таких платформах, как Bloomberg, Figma, Google, Slack, Notion и Vercel.
Настоящий параллелизм
Раньше пропускная способность ограничивалась одним потоком Node. Нативные рабочие процессы устраняют это ограничение. Версия 7 распределяет задачи на анализ, проверку и генерацию результатов, а также добавляет флаги:
--checkers— устанавливает количество параллельных рабочих процессов для проверки типов--builders— параллелизует процесс сборки проектов и стеков в монорепозиториях с использованием--checkers--singleThreaded— объединяет всю работу в одном ядре для отладки и измерения базовых временных показателей
Объем работы по анализу/генерации результатов для каждого файла увеличивается с размером репозитория и степенью его модульности; у крошечных приложений с одним файлом прирост незначителен.
Режим отслеживания также был переписан. Метод опроса загружал процессор при работе с огромными деревьями файлов node_modules; использование механизма отслеживания Parcel, реализованного на Go, снижает эту нагрузку и позволяет быстрее реагировать на изменения.
Изменения в конфигурации, которые создадут проблемы командам
TypeScript 6.0 служил переходным этапом: новые параметры по умолчанию и устаревшие функции отображались как предупреждения. В версии 7.0 они превращаются в серьезные ошибки. Команды, уже прошедшие через версию 6.0, выполнили большую часть работы. Переход с версии 5.x непосредственно на 7.0 требует времени на корректировку файла tsconfig.json.
Основные изменения по умолчанию:
- Параметр
strictвключен по умолчанию, если не указано иное - Параметр
moduleустанавливается в значениеesnext, а параметрtargetвыбирает самую новую стабильную версию ECMAScript, меньшую по номеру, чемesnext - Параметр
noUncheckedSideEffectImportsвключен по умолчанию - Параметр
libReplacementотключен по умолчанию
stableTypeOrdering остаётся включённымrootDir начинается с ./types изначально является пустым спискомВ документации rootDir и types называются теми аспектами, с которыми команды сталкиваются первыми, и теми, для которых решения наиболее просты.
Если файл tsconfig.json находится выше папки src, явно укажите значение rootDir, чтобы структура вывода оставалась знакомой:
{
"compilerOptions": {
"rootDir": "./src"
},
"include": ["./src"]
}
Поскольку types больше не автоматически импортирует всё, что находится в node_modules/@types, укажите только необходимое:
{
"compilerOptions": {
"types": ["node", "jest"]
}
}
Параметры, которые раньше лишь предупреждали, теперь приводят к серьёзным ошибкам (они перестают выполнять какую-либо полезную функцию):
- Удалите
target: es5иdownlevelIteration - Замените устаревшие значения
moduleResolution(node,node10,classic) наnodenextилиbundler - Устраните режимы
module, такие какamd,umd,systemjsиnone, в пользуesnextилиpreserve - Удалите
baseUrlи указывайтеpathsотносительно корня проекта - Не устанавливайте
esModuleInterop/allowSyntheticDefaultImportsв значениеfalse - Считайте, что параметр
alwaysStrictвсегда включен
Если после многих лет без проверки значение moduleResolution по-прежнему равно старому значению node, этот файл служит чек-листом для миграции.
Unicode в типах с литеральными шаблонами
Типы литералов шаблонов теперь определяются по код-точкам Unicode, а не по единицам кода UTF-16:
type HeadTail<S> = S extends `${infer Head}${infer Tail}` ? [Head, Tail] : never;
type Result = HeadTail<"😀abc">;
// In 7.0: ["😀", "abc"]
// Previously: ["\ud83d", "\ude00abc"]
Старые компиляторы могли обрабатывать пару заместителей эмодзи по отдельности. Это соответствовало способу индексации в JS, но редко отражало намерения автора. Теперь итерация происходит в соответствии с код-точками, как это делают конструкции for...of и [...str]. Утилиты, которые намеренно учитывали единицы UTF-16, перестанут работать; все остальные получат желаемое поведение.
Более строгий анализ JavaScript
Раньше проверка файлов .js терпела больше идиом из эпохи JSDoc и Closure. В версии 7 это ограничение сближается с правилами для файлов .ts:
- Места, где требуются типы, отклоняют простые значения — предпочтительнее использовать
typeof someValue @enum/@classтеряют специальную обработку; необходимо объявлять настоящий класс или использовать@typedef- Простой символ
?не считается типом — предпочтительнее использоватьany
! не поддерживаются — необходимо явно указывать Tfunction(string): void заменяются на (s: string) => voidПробел в программной API
В версии 7.0 по-прежнему отсутствует стабильная программная API. Поэтому библиотеки, включающие компилятор — интеграция ESLint с TypeScript, ts-morph, ручно написанные трансформаторы, а также инструменты для редакторов в рамках шаблонов Vue, Svelte, Astro, MDX или Angular — пока не могут полностью перейти на новую версию. Microsoft считает эту проблему временной и ожидает завершения разработки API в версии 7.1 и последующих выпусках.
Тем временем используйте версию 7.0 там, где не требуются плагины language-server. Команды, работающие с Angular, например, могут запускать tsc из версии 7.0 для быстрой проверки всего проекта с помощью CLI, сохраняя при этом версию 6.0 в редакторе. Пакет совместимости @typescript/typescript6 предоставляет бинарник tsc6 и переэкспортирует API версии 6.0, что позволяет им сосуществовать:
{
"devDependencies": {
"typescript": "npm:@typescript/typescript6@^6.0.0"
}
}
Стоит ли обновляться?
Если вы уже используете TypeScript 6.0, миграция требует немногих изменений, но приносит значительные преимущества: достаточно скорректировать несколько параметров tsconfig, чтобы ускорить процессы CI и запуск редактора. При переходе на более старые версии могут возникнуть серьезные проблемы, но почти все из них уже были заранее сообщены.
Установите кандидата на выпуск сегодня же:
npm install -D typescript@rc
Что касается редакторов, сегодня дополнение для VS Code поддерживает протокол LSP, а интеграция с самим VS Code постоянно развивается. Visual Studio распознает версию 7.0 в открытом рабочем пространстве без необходимости отдельной установки.
Разработчики утверждают, что выпуск новых функций снова будет происходить в привычном многомесячном ритме, и ожидается, что версия 7.1 устранит недостатки API для встраивания кода.
Итоговый результат: привычные правила TypeScript, работающие как нативный код.