This TypeScript warning appears when you use decorators without enabling the experimentalDecorators compiler option. The fix is to add 'experimentalDecorators: true' to your tsconfig.json compilerOptions.
The "experimentalDecorators" warning appears when TypeScript encounters decorator syntax (@decorator) in your code but the compiler option to enable decorators hasn't been set. Decorators are a special TypeScript feature that allows you to add annotations and metadata to classes, methods, properties, and parameters. TypeScript supports two types of decorators: 1. **Legacy/Experimental Decorators** - The original implementation based on a TC39 proposal that was never standardized. This requires the experimentalDecorators flag. 2. **Standard Decorators** - The official ECMAScript decorators introduced in TypeScript 5.0, which are now part of the JavaScript standard. This warning specifically refers to legacy decorators, which are still widely used in frameworks like Angular, NestJS, TypeORM, and MobX. When TypeScript sees decorator syntax without the experimentalDecorators flag enabled, it issues this warning because decorators are not enabled by default.
Open your tsconfig.json file and add the experimentalDecorators option to compilerOptions:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}The experimentalDecorators flag enables decorator syntax. The emitDecoratorMetadata flag is optional but often needed for frameworks like Angular, NestJS, or TypeORM that rely on runtime type information.
After saving the file, restart your TypeScript compiler or IDE's TypeScript server:
# Recompile your project
npx tsc
# Or restart your dev server
npm run devIn VS Code, you can manually restart the TypeScript server:
- Press Cmd/Ctrl + Shift + P
- Type "TypeScript: Restart TS Server"
- Press Enter
If you're running tsc with specific file arguments, TypeScript won't read tsconfig.json. Instead, run tsc without arguments:
# WRONG - ignores tsconfig.json
npx tsc src/app.ts src/decorators.ts
# CORRECT - reads tsconfig.json
npx tsc
# Or specify the config file explicitly
npx tsc -p tsconfig.jsonCheck that your files are included in the tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}Verify your file is covered by the include pattern. Files outside the include pattern won't use your tsconfig settings.
Sometimes VS Code needs the setting enabled in both tsconfig.json AND workspace settings:
1. Open VS Code Settings (Cmd/Ctrl + ,)
2. Search for "experimentalDecorators"
3. Find "Implicit Project Config: Experimental Decorators"
4. Check the box to enable it
Alternatively, add to .vscode/settings.json:
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.experimentalDecorators": "enabled"
}After changing settings, restart VS Code or the TypeScript server for changes to take effect.
Projects may have multiple TypeScript config files (tsconfig.json, tsconfig.app.json, tsconfig.spec.json). Ensure experimentalDecorators is set in the correct one:
# List all tsconfig files
find . -name "tsconfig*.json" -not -path "*/node_modules/*"For Angular projects, check tsconfig.app.json:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}For NestJS projects, the default tsconfig.json should have:
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true
}
}Verify which config file your build tool is using (check package.json scripts or build tool config).
If you can't modify tsconfig.json, enable decorators via the command line flag:
# Compile with experimentalDecorators flag
npx tsc --experimentalDecorators
# With emitDecoratorMetadata as well
npx tsc --experimentalDecorators --emitDecoratorMetadata
# Add to package.json scripts
{
"scripts": {
"build": "tsc --experimentalDecorators --emitDecoratorMetadata"
}
}Note: This is a temporary workaround. It's better to fix tsconfig.json so all team members and CI/CD pipelines use the same settings.
### Legacy vs Standard Decorators
TypeScript 5.0+ supports the official ECMAScript decorators standard, which is different from legacy experimental decorators:
Legacy Decorators (experimentalDecorators):
- Based on the original TC39 Stage 2 proposal (2014-2022)
- Used by Angular, NestJS, TypeORM, MobX, InversifyJS
- Requires experimentalDecorators: true
- Decorator functions receive different parameters than standard decorators
Standard Decorators (TypeScript 5.0+):
- Based on the finalized TC39 Stage 3 decorators proposal
- Enabled by default in TypeScript 5.0+
- Different decorator function signature
- Not compatible with libraries built for legacy decorators
Important: You cannot mix both types. If your framework uses legacy decorators, you must enable experimentalDecorators. Most frameworks haven't migrated to standard decorators yet.
### emitDecoratorMetadata Option
The emitDecoratorMetadata option works alongside experimentalDecorators and emits runtime type information:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}When enabled, TypeScript emits type metadata that can be accessed at runtime via the reflect-metadata library:
import "reflect-metadata";
class MyClass {
@LogType
myMethod(param: string): number {
return 42;
}
}
// At runtime, you can read the parameter types:
const paramTypes = Reflect.getMetadata("design:paramtypes", MyClass.prototype, "myMethod");
console.log(paramTypes); // [String]
const returnType = Reflect.getMetadata("design:returntype", MyClass.prototype, "myMethod");
console.log(returnType); // NumberThis is essential for:
- Dependency Injection frameworks (Angular, NestJS, InversifyJS)
- Validation libraries (class-validator, class-transformer)
- ORMs (TypeORM) that need runtime type information
### Common Framework Requirements
Angular:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}NestJS:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs"
}
}TypeORM:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}MobX:
{
"compilerOptions": {
"experimentalDecorators": true
}
}### Decorator Syntax Examples
Class decorators:
@Component({
selector: 'app-root'
})
class AppComponent {}Method decorators:
class UserService {
@Get('/users')
findAll() {}
}Property decorators:
class User {
@Column()
name: string;
}Parameter decorators:
class UserController {
create(@Body() userData: CreateUserDto) {}
}### Migration Considerations
If you're starting a new project, consider:
1. Stick with legacy decorators if using Angular, NestJS, TypeORM - they haven't migrated yet
2. Use standard decorators for greenfield projects without framework dependencies
3. Wait for framework updates before migrating existing projects
Watch for TypeScript 6.0 which may change decorator defaults or deprecate legacy decorators entirely.
### Troubleshooting
If enabling experimentalDecorators doesn't work:
1. Check TypeScript version: Decorators require TypeScript 1.5+
npx tsc --version2. Verify node_modules TypeScript: Your IDE might use a different TypeScript version
ls -la node_modules/typescript3. Clear build cache:
rm -rf dist/
rm -rf .tsbuildinfo4. Check .vscode/settings.json: Local workspace settings can override tsconfig
{
"typescript.tsdk": "node_modules/typescript/lib"
}Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript