Головна / Статті

Перевірка проти виконання: яке місце у вашому інструментарії займає компілятор Go в TypeScript 7

Дізнайтеся, що насправді прискорює вбудований компілятор Go у TypeScript 7, чому інструменти на кшталт Angular та typescript-eslint відстають, і як розділити перевірку типів від виконання коду.

Видавець: REACTAPP.TOP14 хв читання3257 слів

Джерела: TypeScript 7 Moved Its Compiler to Go. Here’s What Actually Changes

TypeScript 7 замінює самостійно розміщений компілятор на нативну версію, написану на Go, і ключові показники є настільки великими, що можуть спричинити плутанину щодо того, що саме стає швидшим. Швидкість вашого продакшн-JavaScript зовсім не змінюється; змінюються швидкість вашого редактора, процедур перевірки типів у системі CI та процесу будови монорепозиторію. Водночас Node, Bun та Vite зробили компілятор TypeScript необов’язковим для запуску та перетворення коду. Цей посібник розділяє ці аспекти, щоб ви могли вирішити, що оновлювати, чого чекати та як структурувати інструментальний набір, де перевірка та виконання є двома окремими завданнями.

Завдяки поширеності TypeScript це має бути зроблено правильно. У серпні 2025 року GitHub повідомив, що TypeScript став найпопулярнішою мовою серед щомісячних учасників спільноти, випередивши як Python, так і JavaScript, причому понад 2,6 мільйона людей щомісяця вносять внески у код на TypeScript. Зміни в основах цієї екосистеми впливають на фреймворки, інструменти перевірки коду, менеджери пакетів та редактори, причому не всі вони розвиваються з однаковою швидкістю.

До версії 7 tsc був програмою, створеною самостійно. Його вихідним кодом був TypeScript, який компілювався у JavaScript, а цей JavaScript запускався на Node для обробки, перевірки та генерації коду для проектів інших користувачів. Самостійний розгортання є традиційним підходом для компіляторів, і саме така архітектура допомогла мові пройти десятиліття стрімкого розвитку. Однак її обмеженням є середовище виконання JavaScript: єдиний основний потік, купа пам’яті з автоматичним збиранням сміття, яка ніколи не налаштовувалась під величезні внутрішні структури з узлами синтаксису та типами, а також обмежені можливості для обміну цими даними між робочими процесами.

Відповідь Microsoft — це порт, а не переписування коду. База коду Go свідомо відтворює структуру та поведінку існуючої реалізації, тому система типів та її семантика мають залишатися незмінними. Змінюється лише основа під ними: нативний машинний код, потоки, які можуть ділитися пам’яттю, та значно більше можливостей для паралельної обробки завдань. Саме з цієї причини Go стало прагматичним вибором; його модель пам’яті та збиральник сміття дозволяють команді перекладати існуючий код з подібними структурами даних, замість того щоб повністю переробляти все на основі правил власності.

Опубліковані тестування показують, що типові повні збірки працюють від 8 до 12 разів швидше:

  • Час збірки коду VS Code скорочується з 125,7 секунд у TypeScript 6 до 10,6 секунд у TypeScript 7.
  • Час повної збірки Playwright скорочується з 12,8 секунд до 1,47 секунди.
  • Налаштування восьми працівників-перевірювачів типів знову знижує час виконання до 7,51 секунди (VS Code) та 1,16 секунди (Playwright).
  • Фраза «TypeScript працює у десять разів швидше» є точним підсумком цих даних. Однак її легко неправильно зрозуміти, адже не вказано, що саме працює у десять разів швидше.

    Що працює швидше, а що — ні

    Візьмемо невеликий, звичайний фрагмент коду: інтерфейс та об’єкт, який йому відповідає.

    interface User {
      id: string;
      name: string;
    }
    const user: User = {
      id: '42',
      name: 'Arthur',
    };
    

    Який би інструмент не компілював цей код, браузер чи сервер у кінцевому підсумку отримують звичайний JavaScript без інформації про типи:

    const user = {
      id: '42',
      name: 'Arthur',
    };
    

    Система виконання не знає, який компілятор обробив вихідний код та на якій мові був реалізований цей компілятор. Вона бачить лише генерований JavaScript, і цей вихід залишається таким самим, як і раніше. Тому переписування tsc на Go ніяк не впливає на швидкість виконання вашого додатку. Якщо сторінка працює повільно або обробник API обмежений потужністю CPU, TypeScript 7 цього не змінить.

    Переваги проявляються раніше, під час розробки, коли TypeScript працює постійно: завантаження проектів, обчислення діагнозів, надання підказок та функції пошуку всіх посилань, відстеження файлів та повторна перевірка результатів компіляції в середовищі CI. Одне показник добре це ілюструє. Раніше в репозиторії VS Code відкриття файлу з помилкою займало близько 17,5 секунд до появи першого діагнозу; з TypeScript 7 цей процес триває менше 1,3 секунди. Новий мовний сервер побудований на основі Language Server Protocol, може використовувати кілька потоків, і Microsoft також повідомляє про значно меншу кількість невдалих команд мовного сервера та збоїв.

    Отже, корисний підхід полягає у наступному: сам додаток не стає швидшим, але робота з ним покращується. У великій базі коду чи монорепозиторії скорочення кількості хвилин на кожну перевірку типів та секунд на кожен діагностичний аналіз у редакторі змінює цикл зворотного зв’язку, у якому ви перебуваєте протягом усього дня. Це справжнє підвищення продуктивності, хоча жоден користувач цього ніколи не помітить.

    Інші новинки версії TypeScript 7

    Нативний компілятор залишається центральним елементом, але кілька підсистем було перебудовано навколо нього.

    Паралелізм, який можна налаштувати

    Парсинг, генерація коду та частини перевірки типів можуть виконуватися паралельно, а процеси компіляції з посиланнями на проекти дозволяють одночасно компілювати кілька проектів, що має велике значення у монорепозиторіях. Ви можете явно збільшити кількість процесів для перевірки типів:

    tsc --checkers 8
    

    Або вимкніть паралелізм повністю, для пристроїв із обмеженими ресурсами чи коли вам потрібна детермінована поведінка під час налагодження:

    tsc --singleThreaded
    

    Окремий прапорець --builders керує кількістю одночасних процесів збірки проекту. Розглядайте їх як налаштовувані елементи, а не як перемикачі „максимум усього“. Кожен додатковий працівник займає в пам’яті свою частку програми, тому CI-завантажувач із багатьма ядрами, але обмеженою кількістю RAM, може працювати повільніше або закінчити роботу через нестачу пам’яті, якщо просто збігати кількість працівників із кількістю ядер. Вимірюйте продуктивність на ваших реальних завантажувачах перед тим, як змінити налаштування.

    Перебудований режим спостереження

    --watch також було перереалізовано. Спочатку команда пробувала більш традиційні міжплатформені підходи, доступні в Go, але опитування файлової системи виявилося занадто ресурсозатратним для великих проектів. У підсумку вони перенесли ідеї з механізму спостереження за файлами у C++ від Parcel до Go та створили новий механізм, спрямований на краще використання ресурсів у різних операційних системах.

    Нові стандартні налаштування та видалені застарілі опції

    TypeScript 7 успадковує зміни конфігурації, внесені під час переходу на TypeScript 6:

    • strict увімкнено за замовчуванням.
    • За замовчуванням тепер використовується esnext для параметра module.
    • types спочатку є порожнім масивом, тому глобальні пакети типів більше не завантажуються автоматично.
    • Параметр target: es5 було видалено.
    • Класичне розв’язування модулів було видалено.
  • Було видалено вихідні дані модулів AMD та UMD.
  • baseUrl більше не підтримується.
  • tsconfig.json, який не змінювався з часів, коли Internet Explorer був важливим фактором розгортання, ймовірно, потребує уваги. Зокрема, зміни в розділі types можуть спричинити безліч помилок типу „не вдається знайти ім’я“ для глобальних об’єктів та вбудованих функцій Node, поки ви не вказуєте пакети, від яких залежите. Якщо ви ще не працювали з цими значеннями за замовчуванням, наш практичний посібник з міграції на типовий синтаксис TypeScript 6.0 детально розглядає їх, і ті самі виправлення підходять і тут.

    Відсутній елемент: стабільний API компілятора

    Найважливішою деталлю є те, чого немає у TypeScript 7.0: стабільний програмний API. Для багатьох проєктів це має більше значення, ніж будь-який флаг чи параметр за замовчуванням, адже саме він визначає, чи зможуть інструменти, пов’язані з компілятором, взагалі функціонувати.

    Чому екосистема не може змінитися миттєво

    Виклик компілятора з терміналу та створення інструменту на основі компілятора — це зовсім різні підходи. Багато фреймворків, інструментів мови та лінтерів не просто використовують бінарний файл без додаткових операцій:

    tsc
    

    Вони завантажують TypeScript як бібліотеку та запитують у нього дерева синтаксису, вирішені типи, відповіді мовного сервісу та іншу внутрішню інформацію компілятора. Заміна цього API очікується у TypeScript 7.1, і до того часу Microsoft офіційно підтримує одночасну установку обох основних версій. На момент написання цього тексту це означає, що багато команд будуть використовувати нативний компілятор для перевірок у командному рядку, тоді як їхні інтеграції з редакторами та лінтери продовжуватимуть завантажувати версію, засновану на JavaScript.

    Це впливає на кілька поширених інструментів:

    • Перевірка типів шаблонів у Angular глибоко інтегрована з TypeScript, тому повна підтримка редактора у Angular поки що мусить залишатися зі старішим мовним сервісом.
    • Інструменти для Vue, Astro, Svelte, MDX та інших форматів, які вбудовують TypeScript у інший тип файлу, стикаються з подібними обмеженнями до моменту випуску нового API.

    Усе це не є недоліком порту. Саме так виглядає міграція фундаментального інструменту в реальній екосистемі: двигун готовий ще до того, як усе, що до нього під’єднується, буде адаптовано.

    Чотири способи, якими інструмент може залежати від TypeScript

    typescript-eslint є гарним прикладом для цього. Сам ESLint не побудований на компіляторі TypeScript, тому твердження про те, що «ESLint став швидшим через перехід TypeScript на Go», є просто неправильним. Однак typescript-eslint є мостом, який дозволяє ESLint аналізувати синтаксис TypeScript та, у випадку правил, заснованих на типах, отримувати інформацію про типи з TypeScript. Його поточний парсер все ще базується на старішому API компілятора, хоча проект експериментує з нативною реалізацією. Розробники явно готуються, але впровадження TypeScript 7 там — це не просто зміна номера версії.

    Цей приклад показує відмінність, яку варто пам’ятати щоразу, коли хтось каже, що інструмент „використовує TypeScript“. Існує принаймні чотири різні форми взаємозв’язку:

    • Написаний на TypeScript. Сам вихідний код інструменту — це TypeScript. Його розробники отримують перевагу від швидшої перевірки типів та компіляції, але користувачі не бачать жодних змін.
    • Виконує TypeScript. Середовища виконання на кшталт Node та Bun приймають файли з розширенням .ts як вхідні дані без участі tsc.
    • Трансформує TypeScript. Збирачі та транспілятори видаляють типи та переписують синтаксис, працюючи з кожним файлом окремо.
    • Залежить від семантики TypeScript. Інструменти для перевірки коду з правилами, що враховують типи, мовні сервіси фреймворків та редактори потребують, щоб компілятор розумів цілий програмний код.

    Кореневий порт змінюється по-різному для кожної категорії. Лише остання потребує API компілятора, і саме ця категорія мусить чекати на версію 7.1.

    Менеджер пакетів під час міграції

    pnpm демонструє, як взаємодіють ці категорії. Це великий монорепо на TypeScript із багатьма пакетами, складними процесами тестування та інтенсивним аналізом у редакторі, що теоретично робить його ідеальним користувачем TypeScript 7. Зрештою так і має бути. Тим часом його адміністратори також мусять підтримувати роботу typescript-eslint, а інтеграція цього лінтера все ще залежить від старої інтерфейсної моделі компілятора. Ланцюг залежностей виглядає приблизно так:

    pnpm
      ↓
    typescript-eslint
      ↓
    TypeScript compiler API
      ↓
    TypeScript 6 — for now
    

    Також варто чітко пояснити, що очікувати від користувачів pnpm. TypeScript 7 не робить цю команду швидшою:

    pnpm install
    

    Встановлення пакетів є частиною функціонування pnpm у режимі виконання, причому pnpm працює як JavaScript у Node, незалежно від того, який компілятор перевіряв його код. TypeScript 7 може покращити процес розробки pnpm: перевірку типів, виконання тестів у середовищі CI та інші процеси, які керуються компілятором. Проте навіть ці покращення з’являються лише з такою швидкістю, якою працює найповільніший важливий елемент у його графі залежностей. Великий проект не може швидше прийняти фундаментальні зміни, ніж інструменти, від яких він залежить.

    Де переваги є негайними

    Проекти, яким потрібен лише компілятор як інструмент перевірки в командному рядку та мовний сервер, можуть отримати користь відразу. VS Code є найяскравішим прикладом: величезна база коду TypeScript, де щодня працюють мільйони розробників, використовуючи інструменти для цієї мови. Зменшення часу повної компіляції з понад двох хвилин до приблизно десяти секунд є значним; скорочення часу очікування першої діагностики в редакторі з приблизно сімнадцяти секунд до трохи більше однієї — це те, що може відчути кожен.

    Playwright, один із проектів Microsoft для тестування, скорочує час повної компіляції за замовчуваною налаштуванням з 12,8 секунд до 1,47 секунди. Це знову ж таки не означає, що Playwright запускає Chromium швидше. Це означає, що підтримка та перевірка власного коду Playwright стають набагато швидшими. Враховуючи те, що багато інструментів для розробників самі написані на TypeScript, швидша підтримка цих інструментів має позитивний ефект на весь екосистему.

    Vite вже розглядає перевірку та трансформацію як окремі завдання

    Vite переважно написаний на TypeScript, але під час обслуговування чи збірки вашого TypeScript-додатку він не викликає компілятор TypeScript для транспіляції кожного модуля. Сучасний Vite використовує спеціалізований механізм трансформації та навмисно пропускає перевірку типів. У його документації рекомендується виконувати TypeScript окремо як одноразову перевірку:

    tsc --noEmit
    

    або безперервно під час розробки:

    tsc --noEmit --watch
    

    Логіка є обґрунтованою. Для трансформації файлу достатньо лише видалити з нього примітки, переписати кілька конструкцій та продовжити. Перевірка типів вимагає знання всієї структури модулів, адже зміна в одному файлі може порушити умови співпраці з іншим. Виконання обох операцій у одному кроці змушує швидку операцію чекати на повільну. Тому типовий проект Vite має два паралельні шляхи від одного й того самого джерела:

    ┌── Vite / Oxc ── transform ──> Browser
    TypeScript source ──┤
                        └── TypeScript ───── type-check
    

    Якщо дивитися з цієї точки зору, TypeScript 7 не повинен ставати найшвидшим трансформатором JavaScript, адже Vite вже має інструмент, оптимізований для цього. TypeScript може зосередитися на завданні, яке вимагає розуміння всієї програми, і тепер це завдання виконується значно швидше. Якщо ви використовуєте таку конфігурацію сьогодні, одним із практичних наслідків є те, що помилка типу не зупиняє сервер розробки; переконайтеся, що команда tsc --noEmit виконується під час CI або у функції перед викладенням коду, щоб помилки не потрапили до продакшну.

    Інструменти JavaScript, написані всім, крім JavaScript

    Тут спостерігається більш широка тенденція. Текуща система збирання Vite використовує Rolldown, і як Rolldown, так і Oxc написані на Rust. esbuild багато років тому обрав Go. Bun реалізує значну частину своєї роботи на Zig. Тепер до них приєднався також TypeScript з використанням Go.

    Це не є критикою JavaScript. Публічні інтерфейси все ще здаються природними для розробників JavaScript та TypeScript, тоді як важке для процесора парсинг, обробка та аналіз виконується у вигляді нативного коду. Різні шари мають різні вимоги, і TypeScript 7 є найяскравішим нещодавнім прикладом вибору мови реалізації для кожного шару.

    Середовища виконання, які працюють з TypeScript без tsc

    Такий самий розподіл відбувається і на рівні середовища виконання. Традиційна уява про проект на TypeScript передбачала пряму лінію від вихідного коду до компілятора, потім до JavaScript та нарешті до Node:

    TypeScript
        ↓
       tsc
        ↓
    JavaScript
        ↓
      Node.js
    

    У реальних проектах завжди було більше кроків, але основна ідея залишалась незмінною: Node виконував JavaScript, тож TypeScript спочатку потрібно було перетворити на JavaScript. Сьогодні це можливо робити безпосередньо:

    node server.ts
    

    Важливою деталлю є те, що Node не запускає компілятор TypeScript. Його вбудована підтримка навмисно є мінімальною: він видаляє синтаксис TypeScript, який можна видалити, та виконує те, що залишилося. Він не здійснює перевірки типів, а синтаксис, який вимагає генерації справжнього JavaScript (а не просто видалення анотацій), не підпадає під цю модель видалення. Типоване оголошення, як-от це:

    const port: number = 3000;
    

    фактично перетворюється на наступне перед виконанням:

    const port = 3000;
    

    Ключовим словом є „Erasable“. Конструкції на кшталт enum, namespace із значеннями під час виконання або параметри конструктора створюють JavaScript-код, якого не було у вихідному коді, тому просте видалення елементів не може з ними впоратися. TypeScript пропонує опцію компілятора erasableSyntaxOnly для позначення таких конструкцій, якщо ви хочете, щоб ваш код залишався сумісним із процесом видалення типів; перевірте актуальну документацію Node та TypeScript для отримання інформації про точну поведінку у ваших версіях.

    Ця можливість існує ще до Node 26, тому пряму обробку TypeScript не слід представляти як функцію Node 26. Node 26 пропонує власні зміни: Temporal за замовчуванням, новіша версія V8, ще одне значне оновлення Undici та звичайна оптимізація після випуску нової версії, але це вже окрема тема. Для цієї дискусії важливим є напрямок розвитку: Node все більше сприймає TypeScript як вхідні дані, які можна обробляти безпосередньо, не запускаючи спочатку tsc для генерації JavaScript.

    Bun сприймає TypeScript як звичайні вхідні дані

    Bun, який досі перебуває у версійній лінії 1.x, працює саме так з самого початку. Запуск файлу TypeScript полягає просто у:

    bun server.ts
    

    Завантажувач Bun видаляє та перетворює синтаксис TypeScript перед виконанням за допомогою власних інструментів. І знову ж таки, транспіляція не є перевіркою типів. Тому цілком логічний робочий процес у Bun складається з двох команд:

    tsc --noEmit
    bun server.ts
    

    Перше питання відповідає на запитання «Чи відповідає ця програма системі типів?» Друге питання відповідає на запитання «Чи може ця програма працювати?» Сучасний Node підтримує таке ж розділення:

    tsc --noEmit
    node server.ts
    

    У обох випадках час виконання ніколи не чекає на перевірку типів, а перевірка типів ніколи не потребує створення файлів вихідних даних. Пам’ятайте, що проект, який працює таким чином, не має кроку збірки для виявлення помилок типів випадково; якщо перевірка не вбудована в CI, це просто не відбудеться.

    Завдання TypeScript — зрозуміти програми

    Протягом багатьох років робочий процес складався з однієї послідовності дій, де компілятор знаходився безпосередньо між написанням та виконанням коду:

    Write TypeScript
          ↓
    Compile TypeScript
          ↓
    Run JavaScript
    

    Сучасні інструменти все більше нагадують дві гілки з одного джерела: одна для розуміння та перевірки, інша — для трансформації та виконання:

    ┌── TypeScript ── understand and check
    TypeScript source ────────┤
                              └── Runtime / bundler ── transform and execute
    

    Vite може використовувати Oxc для трансформації коду, поки tsc перевіряє його. Node самостійно видаляє синтаксис, який можна стерти. Bun має власний лоадер. Жоден з цих інструментів не потребує TypeScript 7 чи будь-якого tsc для запуску вашого додатку. Саме тому нативний компілятор все ще має значення: найважливішим завданням TypeScript більше не є швидка конвертація файлу .ts у файл .js, що й інші інструменти виконують добре. Його справжня цінність полягає у розв’язанні проблем, пов’язаних із всім програмним кодом, таких як:

    • Чи сумісна ця значення з контрактом, до якого воно передається?
    • Чи не пошкодив рефакторинг користувача в іншому пакеті?
    • Чи правильна ця умова генеричних типів?
    • На що посилається цей символ та де ще використовується цей тип?
    • Що має запропонувати редактор у вигляді автодоповнення саме тут?

    Щоб задовольнити ці потреби, потрібно значно більше контексту, ніж просто видалити : string з рядка, і саме аналіз усієї програми — це та частина, яку порт для Go робить значно швидшою. Щоб дізнатися більше про роботу TypeScript нативно в цих інструментах, перегляньте наш посібник з міграції на компілятор TypeScript для Go та нативну екзекуцію.

    Основні висновки

    • TypeScript 7 — це порт існуючого компілятора на Go, а не переробка мови; створюваний JavaScript та продуктивність вашого додатку під час виконання залишаються незмінними.
    • Переваги проявляються під час розробки: повні збірки виконуються в 8–12 разів швидше, діагностика в редакторі відбувається набагато швидше, а сервер мови стає більш стабільним.
  • Налаштуйте параметри --checkers, --builders та --singleThreaded відповідно до реальних обмежень пам’яті у системах CI, замість того щоб сліпо максимізувати кількість працівників.
  • Очікуйте перехідний період. Оскільки стабільна API компілятора буде доступна лише у версії 7.1, Angular, Vue, Svelte, Astro, інструменти MDX та typescript-eslint можуть змусити вас продовжувати використовувати TypeScript 6 для деяких інтеграцій, причому паралельна робота обох версій підтримується.
  • Перевірте файл tsconfig.json на наявність нових стандартних значень та видалених опцій (strict, module, types, ціль ES5, класичне вирішення проблем, AMD/UMD, baseUrl).
  • Розглядайте перевірку типів як окремий крок. Vite, Node та Bun виконують або трансформують код на TypeScript без його перевірки, тому необхідно зробити використання параметра tsc --noEmit обов’язковим у процесі CI.