У сучасній 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 пайплайни великих монорепозиторіїв у десятки разів.