Перейти к основному содержимому

Архитектура компиляции TypeScript и runtime

Разработчику Архитектору

Дальше: TypeScript Server · Форматы и подключение · JS-курс: компилятор TypeScript


TypeScript не выполняется в браузере или Node.js. Компилятор проверяет типы и генерирует JavaScript. Память в runtime управляет движок JS (V8 и др.) через сборщик мусора. TS помогает до запуска — он не заменяет GC и не ловит утечки из замыканий.

Перед этой статьей — Форматы и подключение. После — TypeScript Server. Теория — компиляция и интерпретация, сборка мусора.


Цепочка от исходника до выполнения

Play ITЗагрузка интерактивного демо…

index.ts → tsc (или bundler) → index.js → V8 / браузер

проверка типов
(типы не попадают в .js)
ЭтапЧто происходит
Редактированиеtsserver в IDE показывает ошибки в реальном времени
tsc --noEmitТолько проверка типов (CI)
tsc / Vite buildГенерация .js, опционально .d.ts, .map
node dist/...Выполнение обычного JavaScript

Этапы работы компилятора

Play ITЗагрузка интерактивного демо…

Play ITЗагрузка интерактивного демо…

#ЭтапРезультат
1Лексический анализТокены (function, interface, идентификаторы)
2ПарсингAST (дерево синтаксиса)
3Проверка типовДиагностики, связь типов с узлами AST
4Эмиссия (emit).js, .d.ts, source maps

На этапе эмиссии исчезают:

  • аннотации : string, : User;
  • interface, type (только типы);
  • большая часть generic-параметров.

Сравните вручную src/index.ts и dist/index.js — это лучшее упражнение для понимания границы TS/JS.

Связанный обзор в JS-курсе — компилятор TypeScript.


Транспиляция и проверка типов

В разговоре про TypeScript смешивают два процесса — их важно разделять:

ПроцессЧто делаетРезультат
Проверка типов (typecheck)Сравнивает код с контрактами, строит диагностикиОшибки в IDE / exit code tsc
Транспиляция (emit)Переписывает синтаксис TS/современного JS в целевой targetФайлы .js, .d.ts, source maps
npx tsc --noEmit # только проверка, без emit
npx tsc # проверка + emit в outDir

Bundler (Vite, esbuild) часто транспилирует быстро, но полную проверку графа типов оставляют tsc — типичный скрипт "build": "tsc -b && vite build". Проверка типов всегда на стороне TS; JavaScript в runtime о типах не знает.


Что исчезает при переходе из .ts в .js

src/greet.ts:

interface User {
id: string;
name: string;
}

export function greet(user: User): string {
return `Hello, ${user.name}`;
}

После tsc в dist/greet.js (упрощённо):

export function greet(user) {
return `Hello, ${user.name}`;
}

Разбор:

  • interface User не попадает в JS — это только проверка на этапе компиляции.
  • Параметр user в .js без : User, но IDE и tsc в исходниках знают контракт.
  • Опечатка user.nmae — ошибка до node dist/greet.js, не в runtime.

isolatedModules и verbatimModuleSyntax

Vite, esbuild и Babel транспилируют файлы по отдельности, без полного анализа графа типов как у tsc.

ОпцияНазначение
isolatedModules: trueЗапрещает конструкции, которые нельзя transpile’ить по одному файлу (например, const enum без inline)
verbatimModuleSyntax: trueЯвные import type / export type — типы не смешиваются с value-импортами
// Плохо при isolatedModules + bundler: type-only import как value

import { User } from "./types.js";

// Лучше

import type { User } from "./types.js";

Разбор:

  • В шаблонах Vite react-ts эти флаги часто уже включены — при ошибке cannot be compiled under isolatedModules смотрите синтаксис — import type.

Ключевые опции компилятора

ОпцияНазначение
targetВ какую версию ECMAScript транспилировать синтаксис
moduleФормат import/export на выходе
libКакие глобальные API считать известными (DOM, ES2022)
outDir / rootDirСтруктура выходных файлов
declarationГенерация .d.ts для библиотек
sourceMapКарты для отладки по .ts
incrementalУскорение повторных сборок
compositeProject references в monorepo

Пример для Node:

{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "dist",
"rootDir": "src",
"sourceMap": true,
"declaration": true,
"strict": true
}
}

Разбор:


Роли tsc и bundler

ИнструментТипичная роль
tscПроверка типов; emit JS для Node-библиотек
Vite / esbuild / swcБыстрая сборка frontend, tree-shaking
tsc --noEmitCI: только типы, без файлов
tsc -bСборка monorepo с references

Frontend-проект на Vite часто:

"build": "tsc -b && vite build"

Сначала падает на ошибках типов, затем собирается бандл.

TypeScript 7.0 — нативный компилятор на Go

В июне 2026 Microsoft выпустила TypeScript 7.0: основной пакет typescript на npm теперь содержит нативный компилятор, портированный с исходной реализации на TypeScript/JavaScript в Go (проект typescript-go, ранее — preview tsgo / @typescript/native-preview). Логика type-checking структурно совпадает с TypeScript 6.0; меняются скорость, параллелизм и потребление памяти.

npm install -D typescript # 7.x — обычный tsc
npx tsc --version

Производительность

На крупных open-source репозиториях Microsoft фиксирует ускорение полной сборки примерно в 8–12 раз относительно TypeScript 6.0:

Кодовая базаTS 6TS 7Ускорение
vscode125.7 с10.6 с11.9×
sentry139.8 с15.7 с8.9×
bluesky24.3 с2.8 с8.7×
playwright12.8 с1.47 с8.7×

Память на тех же проектах обычно ниже на 6–26%. В VS Code первая диагностика ошибки в большом файле сократилась с ~17.5 с до <1.3 с. Для monorepo это заметно и в CI (например, у Slack type-check в пайплайне — с ~7.5 мин до ~1.25 мин).

Параллелизация

TypeScript 7 распараллеливает парсинг, type-checking, emit и сборку project references. Новые экспериментальные флаги:

ФлагНазначение
--checkers NЧисло воркеров type-checking (по умолчанию 4). Больше ядер — быстрее, но выше RAM
--builders NПараллельная сборка project references при tsc -b
--singleThreadedОтключить многопоточность (отладка, слабые CI-раннеры)

--checkers и --builders умножаются (--checkers 4 --builders 4 — до 16 воркеров). На CI с малым объёмом памяти имеет смысл уменьшить оба значения.

--watch и редактор

--watch переписан поверх file-watcher из экосистемы Parcel (порт на Go). Language server в 7.0 тоже на новом фундаменте: LSP, многопоточность, меньше падений по сравнению с TS 6. В VS Code — отдельное расширение TypeScript 7; в Visual Studio 7 включается по workspace автоматически.

Совместимость с TypeScript 6.0

Код, который чисто компилируется на TypeScript 6.0stableTypeOrdering и без ignoreDeprecations), на 7.0 должен давать тот же результат. TypeScript 7 наследует новые дефолты 6.0 (strict: true, module: esnext, явный types: [] и др.) и превращает устаревшие флаги в ошибки (moduleResolution: node10, target: es5, baseUrl и т.д.). Перед апгрейдом на 7.0 разумно сначала перейти на 6.0 — release notes 6.0.

Изменение семантики типов: template literal types теперь режут строки по Unicode code points, а не по UTF-16 суррогатным парам ("😀abc"["😀", "abc"], не ["\ud83d", "\ude00abc"]).

Programmatic API и side-by-side

В 7.0 нет стабильного programmatic API (import * as ts from "typescript" для линтеров, трансформеров, Volar). Его обещают в 7.1. Пока инструменты, зависящие от Compiler API, держат TypeScript 6 через пакет @typescript/typescript6 (бинарник tsc6, re-export API 6.0):

{
"devDependencies": {
"@typescript/native": "npm:typescript@^7.0.2",
"typescript": "npm:@typescript/typescript6@^6.0.2"
}
}

npx tsc — проверка на 7.0; typescript-eslint и подобные пакеты продолжают импортировать API 6.0 из typescript.

Vue, Svelte, Astro, Angular

Плагины, встраивающие TypeScript в свой компилятор (Volar и аналоги), пока работают на 6.0. Для Angular возможна схема: tsc 7 в CI + TS 6 в редакторе. В VS Code — команда Disable TypeScript 7 Language Server, чтобы вернуться к 6.0.

Ночные сборки снова публикуются как typescript@next. Подробнее — история TypeScript, TypeScript Server.


Файлы декларации (.d.ts)

При "declaration": true компилятор пишет описание публичного API без реализации:

// dist/user.d.ts (сгенерировано)
export interface User {
id: string;
name: string;
}
export declare function getUser(id: string): User;

Назначение:

  • потребители npm-пакета на TS видят типы без исходников;
  • project references в monorepo ссылаются на .d.ts зависимостей.

Source maps

{
"compilerOptions": {
"sourceMap": true,
"declarationMap": true
}
}
  • sourceMap — отладка .ts в DevTools / Node.
  • declarationMap — переход к исходнику из .d.ts в IDE.

Без карт стек ошибок указывает на dist/*.js — читать сложнее.


Память в runtime (JavaScript)

TypeScript не управляет памятью:

  • В браузере и Node — GC (сборщик мусора).
  • TS не предотвращает утечки — глобальные кэши, забытые подписки, замыкания на большие объекты.
  • TS предотвращает обращение к несуществующему полю, неверные аргументы, часть null-ошибок — до запуска.
ПроблемаЛовит TS?
user.nmae опечаткаДа (если тип User)
Утечка памяти в MapНет
null без проверкиДа при strictNullChecks
Неверный тип в JSON с сервераТолько при явной валидации (Zod и т.п.)

Сравнение GC языков — шпаргалка GC.


noEmit и CI

npx tsc --noEmit
  • Проверяет весь проект по tsconfig.json.
  • Не создаёт dist/ — удобно, когда emit делает Vite.

Отдельный job в CI обычно быстрее полной production-сборки и в логах явно виден как проверка типов.


Project references

Для monorepo:

{
"compilerOptions": {
"composite": true,
"declaration": true,
"incremental": true
},
"references": [{ "path": "../shared" }]
}
tsc -b

Компилятор строит граф пакетов и пересобирает только изменённые части. Подробнее — форматы и подключение — monorepo, справочник — project references.


Частые ошибки

СимптомПричина
В браузере старый кодНе пересобрали / кэш
Breakpoint в .js, не в .tsВыключен sourceMap
Типы есть, runtime падаетTS не валидирует сеть без parse
tsc и Vite дают разный JSРазные target / разный transpiler
Огромный distЛишние файлы в include

Практика

  1. Соберите src/index.tsdist/index.js и выпишите, что исчезло из TS.
  2. Включите sourceMap, поставьте breakpoint в .ts при отладке.
  3. Добавьте в CI tsc --noEmit отдельным шагом.
  4. Сгенерируйте .d.ts (declaration: true) и откройте в IDE Go to definition из другого пакета.
  5. Сравните время tsc и vite build — кто только типы, кто бандл.

Смежные статьи