Question

Understanding the Experimental Decorators Warning in TypeScript

typescriptdecoratorvisual-studio-code

Question

I am getting this TypeScript warning:

Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option to remove this warning.

However, my tsconfig.json already includes:

{
  "compilerOptions": {
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  }
}

What is confusing is that some classes using decorators in the same project do not show the warning, while many others do.

What can cause this inconsistent behavior in the TypeScript compiler?

Short Answer

By the end of this page, you will understand what the experimentalDecorators warning means in TypeScript, why it can still appear even when the option is enabled, and how to troubleshoot configuration, tooling, and project-structure issues that cause inconsistent behavior.

Concept

Decorators in TypeScript are a language feature that must be explicitly enabled. The experimentalDecorators compiler option tells TypeScript that your project intentionally uses decorators.

If that option is enabled and you still see the warning, the most important idea is this:

  • the file may not actually be compiled with the tsconfig.json you think it is
  • a different tool may be compiling the file
  • the editor may be using a different TypeScript project context
  • some files may belong to another tsconfig.json or no project at all

In real projects, TypeScript code is often handled by more than one system:

  • the TypeScript compiler (tsc)
  • your editor's TypeScript language service
  • a bundler such as Webpack, Vite, or esbuild
  • a test runner
  • Babel or another transpiler

That means one part of your setup may respect experimentalDecorators, while another part may ignore it or use a different config.

This matters because many TypeScript problems are not caused by syntax errors. They are caused by configuration scope: which files are included, which config is active, and which tool is reading it.

Mental Model

Think of tsconfig.json like a rulebook for a classroom.

You assume every student is following the same rulebook, but in reality:

  • some students are in a different classroom
  • some are using last week's rulebook
  • some are being taught by a substitute teacher

Decorators warnings often happen for the same reason. You enabled the rule in one tsconfig.json, but some files are being checked under a different rulebook.

So the question is usually not "Did I set experimentalDecorators?" but "Which config is this file actually using?"

Take Quiz

Syntax and Examples

The relevant TypeScript configuration looks like this:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

A simple decorator example:

function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

@sealed
class UserService {
  getUser() {
    return "Alice";
  }
}

What these options do

  • experimentalDecorators: true
    • Allows decorator syntax like @sealed
  • emitDecoratorMetadata: true
    • Emits extra metadata for reflection-based libraries such as Angular or NestJS
    • This does not enable decorators by itself

That means this is not enough:

{
  "compilerOptions": {
    "emitDecoratorMetadata": true
  }
}

You still need:

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

Common hidden issue

A project may contain multiple config files:

tsconfig.json
tsconfig.app.json
tsconfig.spec.json

If one file is compiled using tsconfig.app.json and another is checked using tsconfig.spec.json, their behavior can differ if those configs do not all inherit the same decorator settings.

Step by Step Execution

Consider this project structure:

project/
  tsconfig.json
  src/
    app.ts
  scripts/
    generate.ts

And this tsconfig.json:

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

Now imagine these files:

// src/app.ts
function logClass(target: Function) {}

@logClass
class App {}
// scripts/generate.ts
function logClass(target: Function) {}

@logClass
class Generator {}

What happens

  1. TypeScript reads tsconfig.json.
  2. The include field only includes src/**/*.
  3. src/app.ts is part of the project, so it gets checked with experimentalDecorators: true.
  4. scripts/generate.ts is not part of the project.
  5. Your editor may treat scripts/generate.ts as an isolated file or part of another inferred project.
  6. Since that inferred project does not use your intended config, the decorator warning appears there.

This explains why some files show the warning and some do not, even in the same repository.

Another common path

  1. You open a TypeScript file in your editor.
  2. The editor's TypeScript server decides which tsconfig.json applies.
  3. If no matching config includes the file, it falls back to a default project.
  4. Default settings do not necessarily enable decorators.
  5. The warning appears in the editor, even if tsc from the terminal behaves differently.

Real World Use Cases

Decorators are commonly used in frameworks and libraries that attach metadata or modify behavior.

Common examples

  • Angular
    • @Component, @Injectable, @Directive
  • NestJS
    • @Controller, @Get, @Injectable
  • TypeORM
    • @Entity, @Column, @PrimaryGeneratedColumn
  • Custom app infrastructure
    • logging decorators
    • validation decorators
    • permission decorators
    • caching decorators

Why the warning matters in practice

If one part of your app compiles with decorators enabled and another does not:

  • builds may fail only in certain environments
  • editors may show warnings that CI does not
  • test files may behave differently from app files
  • generated metadata may be missing in one part of the system

This is especially common in monorepos, apps with multiple tsconfig files, and projects using both Babel and TypeScript.

Take Quiz

Real Codebase Usage

In real codebases, developers usually solve this by making configuration explicit and shared.

Common patterns

1. Base config inheritance

Create a shared base config:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Then extend it:

{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "outDir": "dist"
  },
  "include": ["src/**/*"]
}

This avoids one config accidentally missing decorator support.

2. Separate app and test configs

Projects often have:

  • tsconfig.app.json
  • tsconfig.spec.json
  • tsconfig.node.json

Each one should either define or inherit the same decorator settings if those files use decorators.

3. Validate which config is active

A very useful command is:

tsc --showConfig

This shows the final resolved config after extends is applied.

You can also compile a specific project file:

tsc -p tsconfig.json

4. Guard against editor mismatch

Sometimes the terminal build is correct, but the editor still warns. In that case developers often:

  • restart the TypeScript server
  • confirm the editor is using the workspace TypeScript version
  • confirm the file belongs to the expected project

5. Toolchain consistency

If Babel, Jest, ts-node, or a bundler is involved, make sure that tool also supports decorators and is using the expected configuration.

Common Mistakes

1. Assuming emitDecoratorMetadata enables decorators

Broken assumption:

{
  "compilerOptions": {
    "emitDecoratorMetadata": true
  }
}

Why it fails:

  • emitDecoratorMetadata only adds metadata
  • it does not allow @decorator syntax

Fix:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

2. File is outside the include pattern

Broken config:

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

If your decorated file is in tools/ or scripts/, it may not be using this config.

Fix:

{
  "include": ["src/**/*", "scripts/**/*", "tools/**/*"]
}

3. Multiple tsconfig files with different settings

For example:

// tsconfig.app.json
{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}
// tsconfig.spec.json
{
  "compilerOptions": {}
}

Now app files may work while test files warn.

Fix:

  • use a shared base config
  • or copy the needed setting into all relevant configs

4. Editor uses a different TypeScript version

Some editors bundle their own TypeScript version.

This can lead to:

  • different warnings
  • stale diagnostics
  • config resolution differences

Fix:

  • switch to the workspace TypeScript version
  • restart the editor TypeScript service

5. Babel or another tool is handling decorators separately

If you compile through Babel, TypeScript settings may not be the only source of truth.

Fix:

  • verify Babel decorator support is enabled if Babel is transforming the code
  • make sure the TypeScript and Babel setups are compatible

6. Opening a single file outside project context

If you open a file directly, the editor may treat it as an isolated file instead of part of your configured project.

Fix:

  • open the project root folder
  • ensure the file is included by a tsconfig.json
  • check editor project diagnostics

Comparisons

SituationWhat it meansTypical result
experimentalDecorators: trueDecorator syntax is allowed@MyDecorator compiles
emitDecoratorMetadata: true onlyMetadata emission requested, but decorators not enabledWarning or compile error for decorators
File included in active tsconfig.jsonFile uses project compiler optionsConsistent behavior
File not included in active tsconfig.jsonFile may be in inferred projectWarning may appear unexpectedly
Terminal tsc works, editor warnsEditor is using different project/config/versionInconsistent diagnostics
One tsconfig extends anotherShared compiler settings possibleEasier consistency
Multiple unrelated tsconfig filesDifferent settings per subprojectSome files warn, others do not

experimentalDecorators vs emitDecoratorMetadata

OptionPurposeRequired for decorator syntax?
experimentalDecoratorsEnables decorator syntaxYes
emitDecoratorMetadataEmits runtime design metadataNo

tsc vs editor diagnostics

ToolReads config?Can differ?
tsc -p tsconfig.jsonYesUsually predictable
Editor TypeScript serviceYes, but based on detected projectYes
BabelNot based on tsconfig aloneYes
Take Quiz

Cheat Sheet

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Quick rules

  • experimentalDecorators enables decorator syntax
  • emitDecoratorMetadata does not enable decorators by itself
  • a file must belong to the correct TypeScript project to get those settings
  • multiple tsconfig files can cause inconsistent behavior
  • editor warnings may differ from terminal builds

Useful checks

tsc --showConfig
tsc -p tsconfig.json

Things to inspect

  • Is the file inside include?
  • Is the file excluded?
  • Is there another tsconfig closer to that file?
  • Does the project use extends?
  • Is the editor using workspace TypeScript?
  • Is Babel, ts-node, or a bundler compiling the file instead?

Most likely causes of inconsistent warnings

  1. file not included in the expected tsconfig
  2. multiple tsconfig files with different options
  3. editor TypeScript service using a different project
  4. different toolchain for different folders or file types

FAQ

Why do I get the decorators warning even though experimentalDecorators is true?

Usually because the file is not being compiled with the tsconfig.json you edited, or your editor is using a different TypeScript project context.

Why do only some files show the warning in the same project?

Those files may belong to a different tsconfig, be outside the include pattern, or be treated as inferred standalone files by the editor.

Does emitDecoratorMetadata enable decorators in TypeScript?

No. It only emits additional metadata. You still need experimentalDecorators: true.

How can I check which TypeScript config is being used?

Use tsc --showConfig for the active project config, and check your editor's TypeScript project information if available.

Can the problem come from my editor rather than the compiler?

Yes. Editors often run their own TypeScript language service, which can use a different TypeScript version or detect a different project.

Can multiple tsconfig.json files cause this issue?

Yes. This is one of the most common reasons for inconsistent decorator warnings.

Should I restart the TypeScript server after changing tsconfig.json?

Yes. Some editors cache project state, so restarting the TypeScript service can clear stale warnings.

Take Quiz

Mini Project

Description

Build a small TypeScript project with two folders to demonstrate why one decorated file can work while another shows warnings. This helps you understand that compiler options only apply to files that belong to the active TypeScript project.

Goal

Create a project where src files support decorators, then fix configuration so scripts files support them too.

Requirements

  • Create a tsconfig.json that enables experimentalDecorators.
  • Include only the src folder at first.
  • Add one decorated class in src and one in scripts.
  • Update the config so both folders use the same decorator settings.
  • Verify the final setup compiles consistently.
Take Quiz

Keep learning

Related questions

View more