How to fix Decorators are not valid on parameters unless 'emitDecoratorMetadata' is enabled in TypeScript
This TypeScript error occurs when you use parameter decorators without enabling the emitDecoratorMetadata compiler option. Parameter decorators rely on metadata reflection to function properly, which requires both experimentalDecorators and emitDecoratorMetadata to be enabled in tsconfig.json.
Quick fix
Fixes Decorators are not valid on parameters unless 'emitDecoratorMetadata' is enabled
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"target": "ES2020",
"module": "commonjs"
}
}- Enable emitDecoratorMetadata in tsconfig.json
- Install and import reflect-metadata
- Verify tsconfig.json is being used
- Check for TypeScript 5.0+ standard decorator conflicts
{"compilerOptions": {"experimentalDecorators": true,"emitDecoratorMetadata": true,"target": "ES2020","module": "commonjs"}}Decorators are not valid on parameters unless 'emitDecoratorMetadata' is enabled
What this error means
Parameter decorators are a TypeScript feature that allows you to attach metadata to function or method parameters. They are commonly used in dependency injection frameworks like NestJS, Angular, and InversifyJS to automatically resolve dependencies at runtime. When TypeScript compiles code with parameter decorators, it needs to emit type metadata that describes the parameter types. This metadata is stored using the reflect-metadata library and accessed at runtime. Without the emitDecoratorMetadata option enabled, TypeScript cannot emit this critical type information, causing the compiler to reject parameter decorators entirely. This error is distinct from general decorator errors because parameter decorators specifically require metadata reflection. While class, method, and property decorators can work without emitDecoratorMetadata, parameter decorators cannot function without knowing the runtime types of parameters—information only available through metadata emission. The error commonly appears in dependency injection scenarios where frameworks need to know what types to inject into constructor or method parameters.
Typical symptoms
- TypeScript compiler error when using @Inject(), @Param(), or custom parameter decorators
- Error specifically mentions parameter decorators and emitDecoratorMetadata
- Code works in JavaScript but fails TypeScript compilation
- Dependency injection frameworks like NestJS fail to compile
- Parameter decorators on constructor parameters trigger compilation errors
- IDE (VS Code, WebStorm) shows red underlines on parameter decorators
Common causes
- emitDecoratorMetadata is not set to true in tsconfig.json
- experimentalDecorators is disabled (both flags required for parameter decorators)
- Using TypeScript 5.0+ standard decorators which don't support parameter decorators
- tsconfig.json is not being read correctly (wrong directory or extends chain broken)
- reflect-metadata package not installed or imported in entry file
- Multiple tsconfig files with conflicting settings in a monorepo
- Attempting to use parameter decorators with isolatedModules: true (limited support)
How to fix "Decorators are not valid on parameters unless 'emitDecoratorMetadata' is enabled"
Open your tsconfig.json file and ensure both experimentalDecorators and emitDecoratorMetadata are enabled:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"target": "ES2020",
"module": "commonjs"
}
}Both flags are required:
- experimentalDecorators: Enables decorator syntax
- emitDecoratorMetadata: Emits type metadata for parameters
Save the file and restart your TypeScript server in your IDE (VS Code: Cmd+Shift+P → "TypeScript: Restart TS Server").
Parameter decorators require the reflect-metadata polyfill to store and retrieve type information at runtime.
Install it as a dependency (not devDependency):
npm install reflect-metadata
# or
yarn add reflect-metadata
# or
pnpm add reflect-metadataThen import it at the very top of your application entry file (usually main.ts, index.ts, or app.ts):
// main.ts - FIRST import, before anything else
import "reflect-metadata";
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();This polyfill must be loaded before any decorators are evaluated.
Ensure TypeScript is actually reading your tsconfig.json file:
# Check which config file TypeScript is using
npx tsc --showConfigCommon issues:
Wrong directory: Make sure you're running tsc from the directory containing tsconfig.json
Extends chain broken: If you use extends, verify the base config exists:
{
"extends": "./tsconfig.base.json", // Must exist
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}Multiple configs in monorepo: Ensure the correct tsconfig is being used for your package:
# Compile with specific config
npx tsc -p packages/api/tsconfig.jsonTypeScript 5.0 introduced standard ECMAScript decorators, which are NOT compatible with parameter decorators or emitDecoratorMetadata.
If you're using TypeScript 5.0+, you MUST use legacy decorators (experimentalDecorators) for parameter decorators to work:
{
"compilerOptions": {
"experimentalDecorators": true, // REQUIRED for parameter decorators
"emitDecoratorMetadata": true,
"target": "ES2022"
}
}Important: Standard decorators (without experimentalDecorators) do NOT support:
- Parameter decorators at all
- emitDecoratorMetadata
- Design-time type reflection
If you need parameter decorators (for dependency injection), you must use legacy decorators.
Ensure your build tools respect tsconfig.json decorator settings:
For ts-node:
// tsconfig.json
{
"ts-node": {
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
}Or use command line flags:
ts-node --experimental-decorators --emit-decorator-metadata src/main.tsFor webpack with ts-loader:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
options: {
compilerOptions: {
experimentalDecorators: true,
emitDecoratorMetadata: true,
},
},
},
],
},
};For NestJS: The default NestJS tsconfig already includes these settings. If you customized it, restore them.
If you're using isolatedModules: true (common with Babel, esbuild, or SWC), you may encounter limitations with decorators.
Check your configuration:
{
"compilerOptions": {
"isolatedModules": true, // Can cause issues
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}Workaround options:
1. Use tsc for type checking, fast bundler for builds:
// tsconfig.build.json (for esbuild/swc)
{
"extends": "./tsconfig.json",
"compilerOptions": {
"isolatedModules": false // Only for type checking
}
}2. Use SWC with decorator support:
// .swcrc
{
"jsc": {
"parser": {
"syntax": "typescript",
"decorators": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
}
}
}Run type checking separately: npx tsc --noEmit
Advanced notes
### How emitDecoratorMetadata Works
When enabled, TypeScript emits three types of metadata for decorated elements:
class UserService {
constructor(@Inject() private db: DatabaseService) {}
}
// TypeScript emits (conceptually):
// design:type - the type of the decorated element
// design:paramtypes - parameter types (for constructors/methods)
// design:returntype - return type (for methods)For the constructor above, TypeScript emits:
__metadata("design:paramtypes", [DatabaseService])The dependency injection framework reads this at runtime to know to inject a DatabaseService instance.
### NestJS and Dependency Injection
NestJS heavily relies on parameter decorators for dependency injection:
import "reflect-metadata";
import { Injectable, Controller, Get, Param } from "@nestjs/common";
@Injectable()
export class UsersService {
findOne(id: string) {
return { id, name: "John" };
}
}
@Controller("users")
export class UsersController {
// Parameter decorator @Param requires emitDecoratorMetadata
constructor(private usersService: UsersService) {}
@Get(":id")
findOne(@Param("id") id: string) {
return this.usersService.findOne(id);
}
}Without emitDecoratorMetadata, NestJS cannot determine that UsersService should be injected into the constructor.
### Standard Decorators vs Legacy Decorators
TypeScript 5.0 introduced standard ECMAScript decorators, but they have critical limitations:
| Feature | Legacy (experimentalDecorators) | Standard (TS 5.0+) |
|---------|--------------------------------|---------------------|
| Parameter decorators | ✅ Supported | ❌ Not supported |
| emitDecoratorMetadata | ✅ Supported | ❌ Not supported |
| Type reflection | ✅ Available | ❌ Not available |
| Future standard | ⚠️ May change | ✅ ES standard |
For dependency injection frameworks, you MUST use legacy decorators.
### Monorepo Considerations
In monorepos with multiple packages, ensure each package that uses parameter decorators has the correct tsconfig:
monorepo/
├── packages/
│ ├── api/
│ │ └── tsconfig.json ← Must have flags
│ ├── shared/
│ │ └── tsconfig.json ← Must have flags
│ └── web/
│ └── tsconfig.json ← May not need them
└── tsconfig.base.json ← Can set here tooBase config approach:
// tsconfig.base.json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
// packages/api/tsconfig.json
{
"extends": "../../tsconfig.base.json"
}### Performance Implications
emitDecoratorMetadata adds metadata to your compiled JavaScript, slightly increasing bundle size. For most applications this is negligible, but if bundle size is critical:
1. Use tree-shaking to remove unused metadata
2. Only enable in packages that need it
3. Consider alternative DI patterns that don't require decorators
### Debugging Metadata
To verify metadata is being emitted, inspect compiled JavaScript:
npx tsc --outFile output.js src/main.ts
cat output.js | grep "design:paramtypes"You should see lines like:
__metadata("design:paramtypes", [DatabaseService, LoggerService])If these are missing, metadata emission failed.
Frequently asked questions
- What does "Decorators are not valid on parameters unless 'emitDecoratorMetadata'" mean?
- Parameter decorators are a TypeScript feature that allows you to attach metadata to function or method parameters. They are commonly used in dependency injection frameworks like NestJS, Angular, and InversifyJS to automatically resolve dependencies at runtime. When TypeScript compiles code with parameter decorators, it needs to emit type metadata that describes the parameter types.
- How do I fix "Decorators are not valid on parameters unless 'emitDecoratorMetadata'"?
- This TypeScript error occurs when you use parameter decorators without enabling the emitDecoratorMetadata compiler option. Parameter decorators rely on metadata reflection to function properly, which requires both experimentalDecorators and emitDecoratorMetadata to be enabled in tsconfig.json.
- What causes "Decorators are not valid on parameters unless 'emitDecoratorMetadata'"?
- emitDecoratorMetadata is not set to true in tsconfig.json experimentalDecorators is disabled (both flags required for parameter decorators) Using TypeScript 5.0+ standard decorators which don't support parameter decorators
- Is the "Decorators are not valid on parameters unless 'emitDecoratorMetadata'" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- 📚TypeScript Official Documentation
- 📖TypeScript TSConfig - emitDecoratorMetadata(Official Docs)
- 📖TypeScript Handbook - Decorators(Official Docs)
- 📖TypeScript 5.0 Release Notes(Official Docs)
- 🐙