Почему ваш TypeScript кричит про «экспериментальные декораторы»?
Вы когда-нибудь запускали компиляцию TypeScript-проекта и видели в консоли что-то вроде:
Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig.json' or '--experimentalDecorators' to remove this warning.
Если да — вы не одиноки. Этот вопрос на Stack Overflow собрал тысячи просмотров и десятки ответов. Но большинство из них предлагают просто «включить флажок и забыть». А мы сегодня пойдём дальше: разберёмся, что такое experimental decorators warning в TypeScript, откуда оно берётся, как работает под капотом и почему TypeScript вообще считает декораторы «экспериментальными».
Что такое декораторы в TypeScript (и почему они experimental)?
Декораторы — это мощный инструмент метапрограммирования. Они позволяют аннотировать и модифицировать классы, методы, свойства и параметры во время выполнения. Если вы работали с Angular, NestJS или TypeORM, вы уже сталкивались с ними:
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
class AppComponent {}
Но вот в чём загвоздка: JavaScript-стандарт на декораторы (TC39 proposal) до сих пор находится на стадии Stage 3. Это значит, что синтаксис и семантика могут измениться. TypeScript, будучи надмножеством JavaScript, не может просто «заморозить» реализацию — он предлагает экспериментальную поддержку декораторов через флаг experimentalDecorators.
Когда вы включаете эту опцию, компилятор TypeScript генерирует код, который может устареть или сломаться при обновлении стандарта. Именно поэтому он честно предупреждает: «Используешь на свой страх и риск».
Как работает experimental decorators warning под капотом компилятора?
Чтобы понять предупреждение, давайте заглянем в исходники TypeScript. В файле checker.ts (да, это реальный файл внутри компилятора) есть функция checkGrammarForDecorator. Когда парсер встречает декоратор, компилятор проверяет, установлен ли флаг experimentalDecorators в tsconfig.json.
Если флаг не установлен, он генерирует диагностическое сообщение — то самое предупреждение. Оно не является ошибкой (код всё равно скомпилируется), но компилятор настойчиво напоминает: «Эй, ты используешь фичу, которая может измениться!».
Вот как выглядит упрощённая логика:
// Псевдокод реальной логики TypeScript
function checkDecoratorUsage(decorator: Decorator, options: CompilerOptions) {
if (!options.experimentalDecorators) {
reportDiagnostic({
code: 1219,
message: "Experimental support for decorators is a feature that is subject to change in a future release..."
});
}
}
Код ошибки 1219 — это именно то предупреждение, которое вы видите. А код 1220 — аналогичное предупреждение для emitDecoratorMetadata (ещё один экспериментальный флаг, о котором мы поговорим ниже).
Как отключить experimental decorators warning: 3 способа
Самый простой путь — добавить флаг в tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true
}
}
Если вы используете командную строку:
tsc --experimentalDecorators
Или если у вас нет tsconfig.json, можно передать флаг напрямую:
npx tsc --experimentalDecorators src/index.ts
После этого предупреждение исчезнет. Но это лишь верхушка айсберга. Давайте разберёмся, что на самом деле происходит, когда вы включаете experimentalDecorators.
Что генерирует компилятор под капотом?
Когда вы пишете декоратор класса, TypeScript транслирует его в вызов функции во время выполнения. Рассмотрим простой пример:
// Исходный код
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
После компиляции с флагом experimentalDecorators: true, TypeScript сгенерирует примерно такой JavaScript:
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
// ... служебный код для применения декораторов
};
function sealed(constructor) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
let Greeter = class Greeter {
constructor(message) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
};
Greeter = __decorate([sealed], Greeter);
Обратите внимание на функцию __decorate — это хелпер, который TypeScript вставляет в каждый файл с декораторами. Он применяет декораторы снизу вверх (от последнего к первому) и передаёт нужные аргументы в зависимости от типа декоратора (класс, метод, свойство).
Если вы включите ещё и emitDecoratorMetadata: true, компилятор добавит вызовы Reflect.metadata для хранения информации о типах параметров и возвращаемых значений. Это критически важно для фреймворков вроде Angular или NestJS, которые используют Dependency Injection.
Почему это experimental? Реальная история изменений
В 2014 году TypeScript ввёл декораторы, основываясь на раннем черновике TC39. С тех пор предложение несколько раз менялось. В 2022 году появился новый синтаксис декораторов (Stage 3), который отличается от TypeScript-реализации.
Основные отличия нового стандарта от экспериментальной реализации TypeScript:
- Порядок применения: в новом стандарте декораторы применяются сверху вниз, а не снизу вверх.
- Сигнатуры: фабрики декораторов принимают другие аргументы.
- Поддержка: новый стандарт не требует
Reflect.metadata— это отдельный proposal.
TypeScript 5.0 ввёл поддержку нового синтаксиса декораторов (без флага experimentalDecorators), но старый синтаксис пока остаётся для обратной совместимости. Если вы начнёте новый проект, лучше использовать новый синтаксис (без experimental). Если мигрируете старый — готовьтесь к правкам.
Практические кейсы: когда experimental decorators warning — это проблема?
Предупреждение — не ошибка, но оно может вызвать проблемы:
- CI/CD пайплайны: если в настройках стоит
noEmitOnError: true, предупреждение не остановит сборку,