В современной enterprise-разработке и масштабных монорепозиториях (Turborepo, Nx, Lerna) транспиляция JavaScript-кода давно перестала быть узким местом. Нативные инструменты нового поколения на языках Rust и Go (SWC, esbuild, Oxc, Rolldown) преобразуют миллионы строк кода в десятки раз быстрее, чем классический однопоточный компилятор tsc.
Однако в процессе публикации библиотек и пакетов сохранялось критическое узкое место: генерация файлов деклараций типов (.d.ts).
Исторически быстрые пофайловые компиляторы (Single-File Transpilers) не могли генерировать .d.ts файлы. Причина в том, что если экспортируемая функция или константа не имеет явной аннотации типа, компилятор обязан проанализировать реализации всех зависимостей:
// Чтобы сгенерировать .d.ts для этого файла:
import { getUser } from './user-service';
export const currentUser = getUser(); // Каков тип currentUser?Чтобы вычислить тип переменной currentUser, tsc вынужден построить глобальный граф семантики типов всего проекта, перейти в user-service.ts, проанализировать его тело и цепочку импортов. Однофайловые Rust/Go-транспайлеры не имеют доступа к файловой системе соседних модулей и не могут проводить глобальный анализ. Из-за этого на шаге сборки типов CI/CD пайплайн намертво упирался в медленный tsc --declaration.
Решением этой фундаментальной проблемы в TypeScript 5.5+ стал флаг компилятора --isolatedDeclarations.
Флаг --isolatedDeclarations требует явной типизации публичной границы модулей: каждый файл может сгенерировать свой .d.ts полностью автономно. Это разблокирует генерацию типов нативными многопоточными компиляторами (Oxc, SWC, Rolldown) с 30–50-кратным ускорением CI/CD.
Концепция isolatedDeclarations проводит строгое разделение архитектурных зон ответственности:
Если компилятор встречает export function calculate(a: number, b: number) { return a + b; }, режим --isolatedDeclarations выдаст ошибку TS9007: «Экспортируемая функция обязана иметь явный тип возвращаемого значения».
Благодаря этому правилу, нативному генератору (например, Oxc на Rust) больше не нужно парсить тела функций: он считывает только сигнатуры заголовков (export function calculate(a: number, b: number): number) и мгновенно сбрасывает на диск готовый .d.ts.
+-------------------------------------------------------------------------+
| Эволюция генерации файлов деклараций типов (.d.ts) |
+-------------------------------------------------------------------------+
| Традиционный пайплайн (tsc --declaration): |
| [Файл A] ──┐ |
| [Файл B] ──┼──> [tsc Global Type Checker] ──> [.d.ts файлы] |
| [Файл C] ──┘ • Однопоточный, медленный (Node.js рантайм) |
| • Требует построения графа типов ВСЕХ зависимостей |
+-------------------------------------------------------------------------+
│
▼ Флаг --isolatedDeclarations (TS 5.5+)
+-------------------------------------------------------------------------+
| Параллельный нативный пайплайн (Oxc / Rolldown / SWC): |
| [Файл A (Явные типы)] ──> [Поток Rust 1] ──> [Файл A.d.ts] |
| [Файл B (Явные типы)] ──> [Поток Rust 2] ──> [Файл B.d.ts] |
| [Файл C (Явные типы)] ──> [Поток Rust 3] ──> [Файл C.d.ts] |
| • Пофайловая независимая генерация (Single-file emit) |
| • Ускорение сборки типов в 30–50 раз на многоядерных процессорах |
+-------------------------------------------------------------------------+Добавление параметров 'isolatedDeclarations': true и 'declaration': true в секцию compilerOptions.
Запуск tsc --noEmit и добавление явных аннотаций типов возврата на экспортируемые функции и константы.
Гарантия того, что все интерфейсы и DTO, используемые в публичных сигнатурах, имеют ключевое слово export.
Замена вызова tsc --declaration на параллельную генерацию .d.ts через Oxc или Rolldown для мгновенной сборки пакетов.
Почему включение флага isolatedDeclarations запрещает экспорт 'export const config = { retries: 3, timeout: 5000 };' без явного указания типа или as const?
В корпоративных монорепозиториях на сотни пакетов сборка типов через tsc часто занимала более 10–15 минут. Перевод пакетов на --isolatedDeclarations и параллельная генерация типов на Rust (Oxc/Rolldown) сокращает этот этап до 10–20 секунд.
Требование явных аннотаций на экспортируемых функциях защищает библиотеки от случайных ломающих изменений (Breaking Changes): программист не может случайно изменить тип возвращаемого значения публичной функции, просто скорректировав внутреннее ветвление if/else.
Интеграция Rolldown (Rust-бандлера нового поколения) с параллельной генерацией JavaScript-бандла и .d.ts-дерева деклараций за один сверхбыстрый проход без участия Node.js.
Инструменты вроде oxlint умеют автоматически находить отсутствующие аннотации типов на экспортах и с помощью автофиксов подставлять вычисленные типы в код.
Разберем правила оформления кода, совместимого со стандартом isolatedDeclarations, и конфигурацию проекта.
Включение 'isolatedDeclarations': true и 'declaration': true в tsconfig.json проекта.
Компилятор tsc или oxlint проверяет наличие явных типов на всех публичных конструкциях export.
Нативный сборщик Oxc считывает только сигнатуры заголовков, мгновенно сбрасывая реализацию.
Потоки операционной системы синхронно генерируют .d.ts файлы на диск с минимальным расходом памяти.
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
// Включение строгого режима изолированных деклараций (TS 5.5+)
"isolatedDeclarations": true,
// Рекомендуется использовать совместно с isolatedModules
"isolatedModules": true,
"strict": true,
"skipLibCheck": true
}
}export interface UserProfile {
readonly id: string;
readonly name: string;
readonly roles: readonly string[];
}
// ─────────────────────────────────────────────────────────────
// 1. Экспорт функций: ТРЕБУЕТСЯ явный тип возвращаемого значения
// ─────────────────────────────────────────────────────────────
// ❌ ОШИБКА TS9007 при isolatedDeclarations:
// export function fetchUser(id: string) {
// return { id, name: 'Alice', roles: ['admin'] };
// }
// ✅ ПРАВИЛЬНО: Сигнатура явно фиксирует контракт
export function fetchUser(id: string): UserProfile {
return {
id,
name: 'Alice',
roles: ['admin'],
};
}
// ─────────────────────────────────────────────────────────────
// 2. Экспорт констант: ТРЕБУЕТСЯ явная аннотация типа или as const
// ─────────────────────────────────────────────────────────────
export interface ClientConfig {
readonly timeoutMs: number;
readonly retryCount: number;
}
// ✅ Вариант A: Явный тип интерфейса
export const DEFAULT_CONFIG: ClientConfig = {
timeoutMs: 5000,
retryCount: 3,
};
// ✅ Вариант B: Константное утверждение as const
export const HTTP_STATUS = {
OK: 200,
NOT_FOUND: 404,
INTERNAL_ERROR: 500,
} as const;
// ─────────────────────────────────────────────────────────────
// 3. Классы: Публичные члены требуют явных типов
// ─────────────────────────────────────────────────────────────
export class ApiGateway {
// Публичное поле требует явного типа
public readonly endpoint: string;
constructor(endpoint: string) {
this.endpoint = endpoint;
}
// Публичный метод требует явного типа возврата
public getUrl(path: string): string {
return `${this.endpoint}${path}`;
}
// Приватные методы НЕ требуют аннотаций, так как не попадают в .d.ts!
private calculateBackoff(attempt: number) {
return Math.pow(2, attempt) * 100;
}
}Функция объявлена как: 'interface InternalData { hash: string; } export function getHash(): InternalData { ... }'. Почему tsc с флагом isolatedDeclarations выдает ошибку TS9010?
Пример конфигурации монорепозитория с интеграцией Rust-компилятора Oxc для мгновенной генерации деклараций типов .d.ts и раздельной валидацией через tsc --noEmit.
{
"name": "@agenticlab/core-engine",
"version": "1.0.0",
"type": "module",
"scripts": {
"typecheck": "tsc --noEmit",
"build:code": "rolldown -c rolldown.config.ts",
"build:types": "oxc-transform --emit-declarations src -d dist/types",
"build": "pnpm run typecheck && pnpm run build:code && pnpm run build:types"
},
"devDependencies": {
"oxc-transform": "^0.15.0",
"rolldown": "^0.14.0",
"typescript": "^5.5.0"
}
}Если экспортируемая функция возвращает локальный интерфейс без ключевого слова export, однофайловый генератор не сможет создать валидный файл декларации, так как внешний потребитель модуля не сможет разрешить имя типа.
// ❌ ОШИБКА: Локальный интерфейс не экспортирован!
interface SecretConfig { key: string; }
export function loadConfig(): SecretConfig { /* ... */ }
// Ошибка TS9010: Variable must have an explicit type, but its type cannot be named.
// ✅ ПРАВИЛЬНО: Экспорт интерфейса вместе с функцией
export interface SecretConfig { key: string; }
export function loadConfig(): SecretConfig { /* ... */ }Генераторы вроде Oxc и SWC выполняют исключительно синтаксическую генерацию .d.ts по готовым явным аннотациям. Они не производят семантическую проверку типов (Type Checking). Если исключить tsc --noEmit из шагов CI/CD, в продакшен могут попасть грубые логические ошибки несоответствия типов.
Флаг --isolatedDeclarations распространяется исключительно на публичную поверхность модуля (export). Попытки аннотировать каждую внутреннюю переменную внутри функции или приватные методы классов private helper(): void создают ненужный визуальный шум, не давая никакой пользы компилятору.
Флаг --isolatedDeclarations в TypeScript 5.5+ совершил революцию в производительности сборки масштабных кодовых баз. Требуя дисциплины явной типизации публичных экспортов, он позволил экосистеме отвязать генерацию файлов типов .d.ts от медленного процесса tsc и передать ее сверхбыстрым компиляторам нового поколения на Rust (Oxc, Rolldown, SWC), ускорив CI/CD пайплайны крупных монорепозиториев в десятки раз.