Strona główna / Artykuły / TypeScript 7.0 przenosi kompilator na Go dla lepszej szybkości działania

TypeScript 7.0 przenosi kompilator na Go dla lepszej szybkości działania

TypeScript 7.0 przenosi tsc do Go z równoległymi narzędziami sprawdzającymi, nowymi domyślnymi ustawieniami tsconfig, literałami szablonowymi Unicode, bardziej rygorystyczną analizą JS oraz tymczasowym brakiem programowej API.

1086 słów

Przez około czternaście lat kompilator TypeScript mógł sam siebie budować. tsc był wersją TypeScript, która kompilowała się do JavaScript i działała na Node. Gdy ilość linii kodu w repozytoriach wzrosła do milionów, taki model samodzielnego hostowania stał się wąskim gardłem.

TypeScript 7.0 zmienia język hostujący. Kompilator oraz usługa językowa przeniosły się z TypeScript do Go. Microsoft określa to jako przeniesienie, a nie przepisanie – struktura sprawdzania typów pozostaje taka sama jak w wersji 6.0, natomiast wykonywanie polega na kodzie natywnym z paralelizmem opartym na wspólnej pamięci. Opublikowane porównania często wskazują na przyspieszenie o około 10× w porównaniu z TypeScript 6.0.

Jeden z często przytaczanych scenariuszy pokazuje praktyczne znaczenie tej zmiany. Sprawdzanie drzewa projektu w VS Code – obejmującego około 1,5 miliona linii kodu TypeScript – skróciło się z około 77,8 sekundy do około 7,5 sekundy.

Jeśli wcześniejsze wersje używały kody nazwy Project Corsa (przy czym starszy drzewo JavaScript nazywano Strada), to ta wersja stanowi ostateczny rezultat tych działań.

Dlaczego Go, i dlaczego port?

Często spekulowano publicznie na temat języka Rust. Wybrano Go, ponieważ pasowało ono do struktury istniejącego kompilatora: zawierającego złożone struktury grafowe, mechanizm zbierania śmieci oraz struktury cykliczne, których trudno byłoby odtworzyć od zera. Ta zgodność umożliwiła realizację portu linijka po linijce.

Kontekst portowania jest ważniejszy niż sama nazwa języka. Zachowanie architektury pozwoliło na zachowanie dotychczasowych zasad. Projekty, które już przeprowadzają sprawdzanie typów w wersji 6.0 z włączonym stableTypeOrdering oraz bez ignoreDeprecations, powinny uzyskać te same wyniki w wersji 7.0 – szybszy silnik, a nie inny system typów.

Najpierw przeprowadzono testy obciążeniowe produkcji. Przez ponad rok port funkcjonował na potężnych serwerach w takich firmach jak Bloomberg, Figma, Google, Slack, Notion i Vercel przed osiągnięciem tego etapu.

Prawdziwy paralelizm

Kiedyś przepustowość była ograniczona przez pojedynczą wątkę Node. Natywni pracownicy eliminują to ograniczenie. Wersja 7 rozdziela zadania analizy, weryfikacji i generowania wyników oraz dodaje flagi:

  • --checkers określa liczbę równoległych pracowników zajmujących się weryfikacją typów
  • --builders paralelizuje procesy budowania projektów i stosów w monorepo razem z --checkers
  • --singleThreaded skupia wszystko na jednym rdzeniu w celu debugowania i pomiarów czasu wykonywania

Zadania analizy/generowania wyników na poziomie poszczególnych plików skalują się wraz z rozmiarem i modułowością repozytorium; małe aplikacje składające się z jednego pliku odnoszą mniejsze korzyści.

Tryb obserwacji również został przeprojektowany. Metoda pytania (polling) zużywała dużo zasobów CPU w przypadku ogromnych drzew plików node_modules; wprowadzenie mechanizmu obserwacji Parcel napisanego w języku Go zmniejsza to obciążenie i pozwala szybciej reagować na zmiany.

Zmiany w konfiguracji, które sprawią problemy zespołom

TypeScript 6.0 pełnił rolę mostu: nowe ustawienia domyślne i zastąpione funkcje pojawiały się jako ostrzeżenia. W wersji 7.0 stają się one błędami krytycznymi. Zespoły, które już przeszły przez wersję 6.0, zrobiły większość pracy. Przejście bezpośrednio z 5.x na 7.0 wymaga czasu na przygotowanie pliku tsconfig.json.

Warto zwrócić uwagę na następujące zmiany domyślne:

  • strict jest włączone, chyba że zostanie wyłączone
  • module ustawia się na esnext, natomiast target wybiera najnowszą stabilną wersję ECMAScript przed esnext
  • noUncheckedSideEffectImports jest włączone domyślnie
  • libReplacement jest wyłączone domyślnie
  • stableTypeOrdering pozostaje włączone
  • rootDir zaczyna się od ./
  • types jest początkowo pustą listą
  • Dokumentacja wskazuje, że rootDir i types to pierwsze problemy, z którymi borykają się zespoły — i te, które da się naprawić najłatwiej.

    Jeśli plik tsconfig.json znajduje się nad folderem src, ustaw rootDir wyraźnie, aby struktura wyjściowa pozostała znajoma:

    {
      "compilerOptions": {
        "rootDir": "./src"
      },
      "include": ["./src"]
    }
    

    Ponieważ types nie importuje już automatycznie wszystkiego z node_modules/@types, wymień to, czego potrzebujesz:

    {
      "compilerOptions": {
        "types": ["node", "jest"]
      }
    }
    

    Opcje, które wcześniej wydawały ostrzeżenia, teraz powodują poważne błędy (przestają wykonywać jakiekolwiek użyteczne działania):

    • Usuń target: es5 i downlevelIteration
    • Zastąp stare wartości moduleResolution (node, node10, classic) wartościami nodenext lub bundler
    • Zrezygnuj z trybów module, takich jak amd, umd, systemjs i none, na rzecz esnext lub preserve
    • Usuń baseUrl i określ paths względem korzenia projektu
    • Nie ustawiaj esModuleInterop / allowSyntheticDefaultImports na false
    • Traktuj alwaysStrict jako stale włączone

    Jeśli po latach bez rewizji moduleResolution nadal ma wartość starego typu node, ten plik stanowi listę kontrolną migracji.

    Unicode w typach literów szablonowych

    Typy literów szablonowych teraz opierają się na punktach kody Unicode zamiast na jednostkach kody 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"]
    

    Stare kompilatory mogły analizować parę zastępczą emoji. Działo się to w sposób podobny do indeksowania w JS i rzadko odpowiadało intencji autora. Teraz iteracja odbywa się na podstawie punktów kody, tak jak to robią for...of i [...str]. Narzędzia, które celowo liczyły jednostki UTF-16, przestaną działać; wszyscy inni otrzymają zachowanie, którego oczekiwali.

    Strukturalniejsza analiza JavaScript

    Kontrola plików .js wcześniej tolerowała więcej idiomów z okresu JSDoc i Closure. Wersja 7 bardziej zbliża się do zasad stosowanych w plikach .ts:

    • Miejsca wymagające określenia typów odrzucają wartości bez określenia typu — należy używać typeof someValue
    • @enum / @class tracą specjalne traktowanie; należy zadeklarować rzeczywistą klasę lub użyć @typedef
    • Pusty ? nie jest akceptowany jako typ — należy używać any
  • Aserty z końca zawierające ! nie są obsługiwane — należy wyraźnie napisać T
  • Stare formy function(string): void zostały zastąpione przez (s: string) => void
  • Luka w programowalnej API

    W wersji 7.0 nadal brakuje stabilnej programowalnej API. Dlatego biblioteki, które zawierają kompilator — integracja ESLint z TypeScriptem, ts-morph, ręcznie tworzone transformery oraz narzędzia pomocnicze dla szablonów Vue, Svelte, Astro, MDX czy Angular — nie mogą jeszcze całkowicie przeprowadzić zmiany. Microsoft traktuje tę lukę jako tymczasową i przewiduje uzupełnienie API w wersji 7.1 oraz późniejszych wydaniach.

    Tymczasem należy używać wersji 7.0 tam, gdzie nie są wymagane pluginy language-server. Zespoły Angulara mogą na przykład używać tsc z wersji 7.0 do szybkich sprawdzeń na poziomie całego projektu za pomocą interfejsu wiersza poleceń, jednocześnie zachowując wersję 6.0 w edytorze. Pakiet kompatybilności @typescript/typescript6 dostarcza binarnik tsc6 i ponownie eksportuje API wersji 6.0, dzięki czemu obie wersje mogą współistnieć:

    {
      "devDependencies": {
        "typescript": "npm:@typescript/typescript6@^6.0.0"
      }
    }
    

    Czy powinieneś zaktualizować?

    Już przy TypeScript 6.0 migracja jest prosta, a korzyści duże: wystarczy dostosować kilka ustawień tsconfig, aby uzyskać szybsze testy CI oraz szybsze uruchamianie edytora. W przypadku starszych wersji problemy są rzeczywiste, ale prawie wszystkie zostały już zgłoszone.

    Zainstaluj wersję kandydatkę na wydanie jeszcze dziś:

    npm install -D typescript@rc
    

    Dla edytorów dodatek do VS Code obsługuje obecnie standard LSP, a integracja z samym VS Code stale się rozwija. Visual Studio rozpoznaje wersję 7.0 w otwartym workspace bez konieczności osobnego kroku instalacji.

    Założyciele projektu mówią, że wydawanie nowych funkcji wraca do znanego wielomiesięcznego rytmu, a wersja 7.1 ma zamknąć lukę w API do embeddingów.

    Ostateczny efekt: znane zasady TypeScript, działające jako kod natywny.