This error occurs when using ts-node with decorators in TypeScript. The emitDecoratorMetadata compiler option requires experimentalDecorators to be enabled first. The fix involves updating your tsconfig.json to enable both options or adjusting your TypeScript configuration to properly support decorator metadata emission.
The error "ts-node does not support 'emitDecoratorMetadata' without 'experimentalDecorators'" appears when you're using ts-node to run TypeScript code that uses decorators with metadata emission. This is a configuration issue where your TypeScript compiler options are incomplete or inconsistent. TypeScript's decorator system has two related options: 1. **experimentalDecorators**: Enables decorator syntax support (required for using decorators) 2. **emitDecoratorMetadata**: Emits design-time type metadata for decorators (requires experimentalDecorators to be enabled first) The error occurs because ts-node validates that these options are properly configured before attempting to compile and run your code. When emitDecoratorMetadata is set to true but experimentalDecorators is false or not set, ts-node rejects the configuration as invalid. This is particularly common in frameworks like NestJS, TypeORM, or class-validator that rely on decorator metadata for dependency injection, validation, or ORM mapping. The metadata includes type information that's needed at runtime for these frameworks to work properly.
First, ensure both experimentalDecorators and emitDecoratorMetadata are properly set in your tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
// Other compiler options...
"target": "ES2022",
"module": "commonjs",
"strict": true
}
}The order doesn't matter, but both must be present and experimentalDecorators must be true when emitDecoratorMetadata is true.
If you're using a framework like NestJS, check the framework's default tsconfig.json:
# For NestJS projects
cat tsconfig.json | grep -A2 -B2 "experimentalDecorators"
cat tsconfig.json | grep -A2 -B2 "emitDecoratorMetadata"After updating tsconfig.json, restart your ts-node process.
ts-node may be using a different tsconfig file than you expect. Check which configuration file is being loaded:
# Run with --show-config to see what ts-node is using
npx ts-node --show-config
# Or specify the tsconfig explicitly
npx ts-node -P tsconfig.json your-file.ts
# For projects with multiple configs (like tsconfig.build.json)
npx ts-node -P tsconfig.build.json your-file.tsIf you have multiple tsconfig files, ensure consistency:
// tsconfig.json (development)
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
// tsconfig.build.json (production)
{
"extends": "./tsconfig.json",
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}Always check that all extended configurations have the required options.
Command-line flags can override tsconfig.json settings. Verify your package.json scripts or execution commands:
// package.json - WRONG example
{
"scripts": {
"start": "ts-node --compiler-options '{"emitDecoratorMetadata":true}' src/main.ts"
}
}The above is missing experimentalDecorators. Use either:
// Option 1: Specify both options
{
"scripts": {
"start": "ts-node --compiler-options '{"experimentalDecorators":true,"emitDecoratorMetadata":true}' src/main.ts"
}
}
// Option 2: Use tsconfig.json (recommended)
{
"scripts": {
"start": "ts-node -P tsconfig.json src/main.ts"
}
}For frameworks with custom CLI tools (like NestJS):
# Check nest-cli.json configuration
cat nest-cli.json
# Ensure it references the correct tsconfig
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"tsConfigPath": "tsconfig.json"
}
}Older versions of ts-node or TypeScript may have different behavior. Update to compatible versions:
# Check current versions
npx ts-node --version
npx tsc --version
# Update packages
npm install --save-dev typescript@latest ts-node@latest
# Or update with specific versions
npm install --save-dev [email protected] [email protected]Version compatibility matrix:
- TypeScript 5.x works with ts-node 10.x
- TypeScript 4.x works with ts-node 9.x or 10.x
- Avoid mixing major version mismatches
If updating doesn't help, try clearing npm cache and reinstalling:
npm cache clean --force
rm -rf node_modules package-lock.json
npm installFor advanced use cases, configure ts-node programmatically in your entry file:
// register.ts or main.ts
import { register } from 'ts-node';
register({
compilerOptions: {
experimentalDecorators: true,
emitDecoratorMetadata: true,
target: 'ES2022',
module: 'commonjs'
}
});
// Now import your application
import { AppModule } from './app.module';
// ... rest of your codeOr use ts-node/register with require:
// In your main JavaScript file
require('ts-node').register({
compilerOptions: {
experimentalDecorators: true,
emitDecoratorMetadata: true
}
});
require('./src/main.ts');For Jest testing with ts-jest:
// jest.config.js
module.exports = {
preset: 'ts-jest',
globals: {
'ts-jest': {
tsconfig: {
experimentalDecorators: true,
emitDecoratorMetadata: true
}
}
}
};If ts-node continues to have issues, consider compiling with tsc first, then running the JavaScript:
# Compile TypeScript to JavaScript
npx tsc
# Run the compiled JavaScript
node dist/main.jsOr use a build process with watch mode:
{
"scripts": {
"build": "tsc",
"start": "node dist/main.js",
"dev": "concurrently "tsc --watch" "nodemon dist/main.js""
}
}Install required packages:
npm install --save-dev concurrently nodemonFor development with hot reload, consider ts-node-dev:
npm install --save-dev ts-node-dev{
"scripts": {
"dev": "ts-node-dev --respawn --transpile-only src/main.ts"
}
}ts-node-dev automatically handles tsconfig.json and may have different error handling.
### Understanding Decorator Metadata
When emitDecoratorMetadata is enabled, TypeScript emits type metadata that can be accessed at runtime using the reflect-metadata library:
import 'reflect-metadata';
class Example {
constructor(@Inject() private service: Service) {}
}
// At runtime, you can access:
const paramTypes = Reflect.getMetadata('design:paramtypes', Example);
// Returns [Service] - the type informationThis metadata is used by:
- NestJS: For dependency injection (determining what to inject)
- TypeORM: For mapping TypeScript types to database columns
- class-validator: For validation rules based on type information
- class-transformer: For object transformation
### TypeScript 5.0+ and Decorators
TypeScript 5.0 introduced changes to decorators that may affect this error:
1. New decorators proposal: TypeScript 5.0+ supports both old (experimental) and new (standard) decorators
2. Metadata emission differences: New decorators may have different metadata behavior
3. Configuration: For new decorators, use "experimentalDecorators": false and enable via "useDefineForClassFields"
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true,
"experimentalDecorators": false,
"emitDecoratorMetadata": false // May not be supported with new decorators
}
}### Framework-Specific Considerations
NestJS:
- Requires both options enabled
- Uses reflect-metadata polyfill
- Check nest-cli.json for compiler options overrides
TypeORM:
- Needs metadata for entity column types
- Works with both experimental and new decorators (in newer versions)
- May require additional reflect-metadata configuration
Angular:
- Uses its own decorator system (not TypeScript's experimentalDecorators)
- Angular CLI manages tsconfig.json automatically
- Rarely encounters this specific ts-node error
### Debugging ts-node Configuration
To debug what ts-node is seeing:
# Show full configuration
TS_NODE_DEBUG=true npx ts-node --show-config your-file.ts
# Check environment variables affecting ts-node
env | grep TS_NODE
# Common environment variables:
# TS_NODE_COMPILER_OPTIONS - JSON string of compiler options
# TS_NODE_PROJECT - Path to tsconfig.json
# TS_NODE_FILES - Whether to include files from tsconfig### Performance Considerations
emitDecoratorMetadata increases bundle size and may affect compilation performance. Consider disabling it in production if not needed:
// tsconfig.build.json (production)
{
"extends": "./tsconfig.json",
"compilerOptions": {
"emitDecoratorMetadata": false // Disable for smaller bundles
}
}However, frameworks requiring metadata (like NestJS) won't work without it.
### Migration from JavaScript Decorators
If migrating from Babel or other transpilers that handled decorators differently:
1. Ensure reflect-metadata is installed: npm install reflect-metadata
2. Import it at the entry point: import 'reflect-metadata';
3. Verify tsconfig.json matches your previous decorator transformation settings
4. Test that runtime metadata access works as expected
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