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, але виконання відбувається за допомогою нативного коду з паралелізмом через спільну пам’ять. У опублікованих порівняннях часто фіксується прискорення приблизно у 10× порівняно з TypeScript 6.0.
Один із часто наводжених прикладів демонструє конкретну користь від цих змін. Перевірка коду у 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об’єднує всю роботу в один ядро для дебаггінгу та вимірювання базових часів виконання
Обсяг роботи з аналізу/виведення даних за кожним файлом зростає разом із розміром та модульністю репозиторію; дуже маленькі однокористувацькі додатки отримують меншу користь від цього.
Режим спостереження також було оновлено. Метод опитування споживав велику кількість ресурсів CPU у великих деревах node_modules; використання механізму спостереження Parcel у Go знижує цей ресурсне навантаження та дозволяє швидше реагувати на зміни.
Зміни конфігурації, які створять труднощі для команд
TypeScript 6.0 виступив посередником: нові значення за замовчуванням та скасувані функції з’являлися як попередження. У версії 7.0 вони стають серйозними помилками. Команди, які вже пройшли версію 6.0, виконали більшу частину роботи. Перехід від версії 5.x безпосередньо до 7.0 вимагає часу на очищення файлу tsconfig.json.
Основні зміни за замовчуванням:
strictувімкнено, якщо не було вказано іншеmoduleвстановлюється наesnext, тоді якtargetобирає найновішу стабільну версію ECMAScript, яка є меншою заesnextnoUncheckedSideEffectImportsувімкнено за замовчуванням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. Тому бібліотеки, які вбудовують компілятор — інтеграція TypeScript у ESLint, 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, які працюють як нативний код.