Avatar ·

Предупреждение об экспериментальных декораторах в TypeScript

Всем привет! Я новичок в TypeScript и пытаюсь использовать декораторы для добавления логирования в свой класс. У меня установлен Visual Studio Code, и я создал простой проект с файлом tsconfig.json. Вот мой код: ```typescript function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function(...args: any[]) { console.log(`Вызван метод ${propertyKey} с аргументами:`, args); return originalMethod.apply(this, args); }; return descriptor; } class Calculator { @log add(a: number, b: number): number { return a + b; } } const calc = new Calculator(); calc.add(2, 3); ``` При компиляции я получаю предупреждение: "Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning." Я попробовал добавить `"experimentalDecorators": true` в tsconfig.json, но предупреждение всё равно появляется. Как это исправить? И стоит ли вообще использовать декораторы, если они экспериментальные? Буду благодарен за подробное объяснение! <<>>

Причина предупреждения и решение

Вы столкнулись с типичной ситуацией. Предупреждение возникает, потому что декораторы в TypeScript всё ещё считаются экспериментальной функцией. Хотя они широко используются в фреймворках (Angular, NestJS, TypeORM), стандарт ECMAScript до сих пор не утвердил их окончательную спецификацию. TypeScript реализовал их раньше стандарта, но с пометкой "экспериментально".

Чтобы убрать предупреждение, нужно правильно настроить tsconfig.json. Вот минимальная рабочая конфигурация:

{
  "compilerOptions": {
    "target": "ES6",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true, // опционально, для получения метаданных
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Проверка корректности

Убедитесь, что:

  • Файл tsconfig.json находится в корневой папке проекта (где лежит package.json).
  • В VSCode используется верный TypeScript — не встроенный, а из node_modules (см. настройку typescript.tsdk).
  • После добавления опции перезапустите компиляцию: npx tsc --watch или через Ctrl+Shift+B.

Почему декораторы экспериментальные?

Декораторы — это предложение для JavaScript (stage 2 на момент написания). TypeScript добавил их раньше, но синтаксис может измениться. Пример различий:

  • Текущий TypeScript-синтаксис (экспериментальный): @log над методом.
  • Будущий стандарт может использовать другой подход (например, декораторы как функции высшего порядка).

Поэтому Microsoft честно предупреждает: "используйте на свой страх и риск, API может поменяться". Однако для современных проектов это безопасно — Angular и NestJS полностью на них завязаны, и они вряд ли сломают обратную совместимость.

Полный пример рабочего кода

После настройки tsconfig ваш код будет компилироваться без предупреждений. Вот улучшенная версия декоратора с поддержкой асинхронных методов:

function log(
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
) {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
        console.log(`[LOG] Вызван ${propertyKey} с аргументами:`, args);
        const start = Date.now();
        const result = originalMethod.apply(this, args);
        const duration = Date.now() - start;
        console.log(`[LOG] ${propertyKey} завершён за ${duration}мс, результат:`, result);
        return result;
    };

    return descriptor;
}

class Calculator {
    @log
    add(a: number, b: number): number {
        return a + b;
    }

    @log
    multiply(a: number, b: number): number {
        return a * b;
    }
}

const calc = new Calculator();
console.log(calc.add(2, 3)); // Логи: вызов, завершение, результат 5
console.log(calc.multiply(4, 5)); // Логи: вызов, завершение, результат 20

Альтернатива без декораторов

Если вы не хотите использовать экспериментальную функциональность, можно обойтись функциями-обёртками:

function withLogging(method: Function) {
    return function (...args: any[]) {
        console.log(`Вызван метод с аргументами:`, args);
        return method(...args);
    };
}

class Calculator {
    add = withLogging(function(a: number, b: number) {
        return a + b;
    });
}

const calc = new Calculator();
calc.add(2, 3); // Работает без предупреждений

Но этот подход менее удобен для множества методов.

Рекомендация

Используйте декораторы смело — они стабильны в TypeScript уже много лет. Просто добавьте "experimentalDecorators": true в tsconfig.json, и всё будет работать. Если проект строго требует "чистого" ES-стандарта, подождите окончательной спецификации (ожидается в ES2024+).

<<>>

Дополнительный подход: настройка через jsconfig.json для VSCode

Если вы используете Visual Studio Code для JavaScript-проекта (не TypeScript), настройка делается аналогично через файл jsconfig.json. Создайте его в корне проекта:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "target": "ES6"
  },
  "include": ["src/**/*"]
}

После этого VSCode перестанет показывать предупреждение во время редактирования.

Почему предупреждение может оставаться

Иногда VSCode использует свою встроенную версию TypeScript, которая не читает tsconfig. Чтобы это исправить:

  • Нажмите Ctrl+Shift+P → TypeScript: Select TypeScript Version.
  • Выберите Use Workspace Version (версию из node_modules).

Если вы используете ts-node для запуска, добавьте флаг в package.json:

"scripts": {
  "start": "ts-node --experimentalDecorators src/index.ts"
}

Когда стоит избегать декораторов

Если ваш проект — библиотека с открытым исходным кодом, которую будут использовать другие разработчики, лучше избегать экспериментальных декораторов. Вместо этого используйте паттерн "фабрика функций" или HOF (higher-order functions). Это гарантирует совместимость с любыми версиями TypeScript и JavaScript.

Пример для библиотеки:

// Вместо @log используем функцию-декоратор
function withLogging(fn: T): T {
    return ((...args: any[]) => {
        console.log(`Вызов функции: ${fn.name}`);
        return fn(...args);
    }) as T;
}

const add = withLogging((a: number, b: number) => a + b);

Такой код работает везде без предупреждений и не требует экспериментальных опций.