NestJS Module 1 – Getting started with NestJS 12
Decorators, metadata and why Nest needs a build
How decorators and emitDecoratorMetadata tell Nest what a constructor needs, why Node.js cannot run Nest source directly, and three traps that erase it.
What you will learn
- Explain how experimentalDecorators and emitDecoratorMetadata let Nest find constructor dependencies
- Show why Node.js's built-in type stripping cannot run Nest source files
- Fix the import type, interface and missing-decorator mistakes that erase dependency metadata
- Choose the TypeScript version that Nest 12's tooling supports
Before you start
What you’ll learn · Before you start
What you will learn
- Explain how experimentalDecorators and emitDecoratorMetadata let Nest find constructor dependencies
- Show why Node.js's built-in type stripping cannot run Nest source files
- Fix the import type, interface and missing-decorator mistakes that erase dependency metadata
- Choose the TypeScript version that Nest 12's tooling supports
Before you start
On this page
Run a Nest source file with node and it stops before its first line of logic, even on Node.js 24, which runs
TypeScript files by itself. Compile it without one compiler option and Nest starts happily, then throws
Cannot read properties of undefined the first time a service uses what it was given.
Both failures come from the same place: Nest learns what each constructor needs from data that only a TypeScript compiler writes. This lesson shows that data in the compiled output, runs Nest source through Node.js’s own type stripping to see where it breaks, reproduces three mistakes that erase the data, and settles which TypeScript version a Nest 12 project should use.
A decorator is a function the compiler calls for you
The examples of this lesson use one small file, kitchen.ts: a menu service, a clock and an OrdersService whose
constructor asks for both.
import { Injectable } from '@nestjs/common';
@Injectable()
export class MenuService {
today() {
return ['poha', 'dal rice', 'thepla'];
}
}
@Injectable()
export class Clock {
now() {
return '10:15';
}
}
@Injectable()
export class OrdersService {
constructor(
private readonly menu: MenuService,
private readonly clock: Clock,
) {}
summary() {
return `${this.clock.now()}: ${this.menu.today().join(', ')}`;
}
} @Injectable() is a decorator: A function applied with @ to a class, method, property or parameter, which can record data about it or change it. Nest uses TypeScript's experimental decorators, which a compiler turns into function calls.: a function that receives the class and may record
something about it. JavaScript engines cannot run this syntax yet, so the TypeScript compiler, with
experimentalDecorators on, rewrites it into ordinary calls. The session compiles the file and prints what it wrote
for OrdersService, then lets Nest build the class:
import { session } from '../session.mjs';
// Compiles kitchen.ts, shows what TypeScript wrote for OrdersService, then lets Nest build it.
session(`
npx tsc -p tsconfig.json
sed -n '/^let OrdersService/,$p' dist/kitchen.js
node dist/main.js
`); Output
$ npx tsc -p tsconfig.json
$ sed -n '/^let OrdersService/,$p' dist/kitchen.js
let OrdersService = class OrdersService {
menu;
clock;
constructor(menu, clock) {
this.menu = menu;
this.clock = clock;
}
summary() {
return `${this.clock.now()}: ${this.menu.today().join(', ')}`;
}
};
OrdersService = __decorate([
Injectable(),
__metadata("design:paramtypes", [MenuService,
Clock])
], OrdersService);
export { OrdersService };
$ node dist/main.js
design:paramtypes of OrdersService: [ 'MenuService', 'Clock' ]
OrdersService.summary(): 10:15: poha, dal rice, thepla
Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node run.mjs
Read the compiled class from the top. The constructor’s private readonly menu: MenuService became a field and an
assignment, because parameter properties are TypeScript syntax too. The decorator became
OrdersService = __decorate([Injectable(), __metadata("design:paramtypes", [MenuService, Clock])], OrdersService):
a list of decorator calls applied to the class when the module loads. The second entry of that list is the one Nest
depends on.
Checkpoint
Check: What the compiler makes of decorators
emitDecoratorMetadata records what the constructor needs
Types disappear when TypeScript compiles to JavaScript. emitDecoratorMetadata keeps one kind of them for every
decorated class: the types of its constructor parameters, written as values that exist at run time, under the
design:paramtypes: The metadata key under which TypeScript records the types of a decorated class's constructor parameters, as values that exist at run time: a class, or Object or Function when it cannot name one. key. The reflect-metadata package stores them on the class,
and Nest reads them back at start-up. You never load that package yourself in an app: the entry files of
@nestjs/common and @nestjs/core import it first, so it is in place before any decorated class of yours is
evaluated. Follow the list from the source file to the instance:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- The source. OrdersService asks for a MenuService and a Clock in its constructor. Those types are TypeScript: JavaScript has no place to keep them, so on their own they vanish at compile time.
- The compiler. tsc compiles the class. experimentalDecorators turns @Injectable() into a call to __decorate, and emitDecoratorMetadata asks for one more thing: a record of the constructor parameter types.
- The output. kitchen.js now holds __metadata("design:paramtypes", [MenuService, Clock]): the classes themselves, as values that exist when the program runs.
- The metadata store. When the module loads, __decorate runs, and reflect-metadata stores that list on the OrdersService class under the key design:paramtypes. Any code can read it back with Reflect.getMetadata().
- The container. At start-up Nest reads design:paramtypes for every provider in the module. It finds MenuService and Clock among the module's providers and builds them first.
- The instance. Nest calls new OrdersService(menu, clock) with the two instances, and summary() returns "10:15: poha, dal rice, thepla", as the recorded run printed.
- Without the metadata. Built with emitDecoratorMetadata off, kitchen.js has no __metadata line. Nest finds no parameter list, calls new OrdersService() with nothing, and starts without a word; summary() then throws a TypeError.
That is the whole mechanism of constructor injection: no configuration names MenuService or Clock as the
dependencies of OrdersService; the compiler wrote them down. Here is the same program built with
emitDecoratorMetadata switched off in tsconfig.no-metadata.json:
import { session } from '../session.mjs';
// The same program, built with emitDecoratorMetadata switched off (tsconfig.no-metadata.json).
session(`
npx tsc -p tsconfig.no-metadata.json
sed -n '/^OrdersService = /,$p' dist-no-metadata/kitchen.js
node dist-no-metadata/main.js
`); Output
$ npx tsc -p tsconfig.no-metadata.json
$ sed -n '/^OrdersService = /,$p' dist-no-metadata/kitchen.js
OrdersService = __decorate([
Injectable()
], OrdersService);
export { OrdersService };
$ node dist-no-metadata/main.js
design:paramtypes of OrdersService: undefined
OrdersService.summary() threw TypeError: Cannot read properties of undefined (reading 'now')
Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node no-metadata.mjs
The __metadata line is gone, Reflect.getMetadata finds nothing, and Nest starts without a warning: with no list
of parameter types it calls new OrdersService() with no arguments. The failure waits until summary() touches
this.clock. That delay is what makes missing decorator metadata: Type information that TypeScript writes next to a decorated class when emitDecoratorMetadata is on, such as design:paramtypes. Libraries read it with the reflect-metadata package. expensive to
debug: the error points at the line that used the dependency, not at the build setting that lost it.
Key idea
Nest injects by the parameter types that the compiler records next to each decorated class. If the list is missing, Nest builds the class with nothing and the error shows up later, at first use.
Checkpoint
Check: design:paramtypes and injection
Node.js’s type stripping cannot run Nest source
Node.js 24 runs .ts files directly: type stripping: Running TypeScript by deleting its type annotations instead of compiling it, as Node.js does for .ts files. Syntax that needs a transformation, such as decorators or parameter properties, cannot run that way. is on by default since Node.js
23.6 and stable since 24.12. It works by deleting type annotations and running what is left, which is fine for
plain TypeScript and fatal for Nest. Each of these three runs is meant to fail:
import { session } from '../session.mjs';
// Node.js runs .ts files by deleting their types. Each of these runs is meant to fail; read which line stops it.
session(`
!node src/kitchen.ts
!node src/menu.controller.ts
!node --experimental-transform-types src/kitchen.ts
`); Output
$ node src/kitchen.ts
node:internal/modules/run_main:107
triggerUncaughtException(
^
src/kitchen.ts:12
export class OrdersService {
constructor(private readonly clock: Clock) {}
^^^^^^^^^^^^
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript parameter property is not supported in strip-only mode
at parseTypeScript (node:internal/modules/typescript:68:40)
at processTypeScriptCode (node:internal/modules/typescript:146:42)
at stripTypeScriptModuleTypes (node:internal/modules/typescript:215:22)
at ModuleLoader.<anonymous> (node:internal/modules/esm/translators:680:29)
at #translate (node:internal/modules/esm/loader:438:20)
at afterLoad (node:internal/modules/esm/loader:506:29)
at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:511:12)
at #getOrCreateModuleJobAfterResolve (node:internal/modules/esm/loader:564:36)
at afterResolve (node:internal/modules/esm/loader:611:52)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:617:12) {
code: 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX'
}
Node.js v24.21.0
[exit status 1]
$ node src/menu.controller.ts
src/menu.controller.ts:3
@Controller('menu')
^
SyntaxError: Invalid or unexpected token
at compileSourceTextModule (node:internal/modules/esm/utils:319:16)
at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:89:18)
at ModuleLoader.<anonymous> (node:internal/modules/esm/translators:681:10)
at #translate (node:internal/modules/esm/loader:438:20)
at afterLoad (node:internal/modules/esm/loader:506:29)
at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:511:12)
at #getOrCreateModuleJobAfterResolve (node:internal/modules/esm/loader:564:36)
at afterResolve (node:internal/modules/esm/loader:611:52)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:617:12)
at node:internal/modules/esm/loader:636:32
Node.js v24.21.0
[exit status 1]
$ node --experimental-transform-types src/kitchen.ts
(node:<pid>) ExperimentalWarning: Transform Types is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
src/kitchen.ts:2
@Injectable()
^
SyntaxError: Invalid or unexpected token
at compileSourceTextModule (node:internal/modules/esm/utils:319:16)
at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:89:18)
at ModuleLoader.<anonymous> (node:internal/modules/esm/translators:681:10)
at #translate (node:internal/modules/esm/loader:438:20)
at afterLoad (node:internal/modules/esm/loader:506:29)
at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:511:12)
at #getOrCreateModuleJobAfterResolve (node:internal/modules/esm/loader:564:36)
at afterResolve (node:internal/modules/esm/loader:611:52)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:617:12)
at node:internal/modules/esm/loader:636:32
Node.js v24.21.0
[exit status 1]
Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node run.mjs
The first run stops at private readonly clock: Clock, a parameter property, which needs code generated rather than
types deleted (ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX). The second file has no parameter property, and it stops at the
@ of @Controller('menu'): deleting types leaves the decorator in place, and V8 cannot parse decorators yet. The
third run adds --experimental-transform-types, which does generate code for parameter properties, and still stops
at the @, because Node.js does not transform decorators at all.
| TypeScript syntax | Type stripping | With --experimental-transform-types |
|---|---|---|
Type annotations, interfaces, import type |
runs | runs |
Parameter properties, enum |
fails | runs |
| Decorators | fails | fails |
emitDecoratorMetadata (a tsconfig.json option) |
not applied | not applied |
Even if V8 parsed decorators, the last row would remain: Node.js does not read tsconfig.json, so nothing would
write design:paramtypes. A Nest project always compiles first, with tsc through nest build, or with SWC, which
the NestJS documentation describes as about 20 times faster. nest start -b swc uses it, and SWC writes the same
metadata once its configuration turns on legacyDecorator and decoratorMetadata; it does not check types, so keep
tsc running as well, with --type-check or in CI.
Checkpoint
Check: Why type stripping cannot run Nest
Three ways to lose the metadata, and the fix for each
The compiler writes a parameter’s type as a value only when it can name one. Three common changes leave it nothing to name. This program tries five services that each need a clock: three with a mistake, two with the fix:
TypeScript (services.ts) · traps/src/services.ts
import { Injectable } from '@nestjs/common';
import type { Clock as ClockType } from './clock.js';
import { Clock, ClockLike, type ClockShape } from './clock.js';
// 1. The class is imported with "import type": only its type survives compilation.
@Injectable()
export class ImportTypeService {
constructor(private readonly clock: ClockType) {}
cutOff() {
return `orders close at ${this.clock.now()}`;
}
}
// 1, fixed: a normal import keeps the class, so the compiler can record it.
@Injectable()
export class ImportValueService {
constructor(private readonly clock: Clock) {}
cutOff() {
return `orders close at ${this.clock.now()}`;
}
}
// 2. The parameter is typed with an interface.
@Injectable()
export class InterfaceService {
constructor(private readonly clock: ClockShape) {}
cutOff() {
return `orders close at ${this.clock.now()}`;
}
}
// 2, fixed: an abstract class is the token; the module says which class provides it.
@Injectable()
export class AbstractClassService {
constructor(private readonly clock: ClockLike) {}
cutOff() {
return `orders close at ${this.clock.now()}`;
}
}
// 3. No @Injectable(): the compiler records nothing for an undecorated class.
export class UndecoratedService {
constructor(private readonly clock: Clock) {}
cutOff() {
return `orders close at ${this.clock.now()}`;
}
} TypeScript (clock.ts) · traps/src/clock.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class Clock {
now() {
return '10:15';
}
}
// An abstract class exists at run time, so it can stand for "any clock" as an injection token.
export abstract class ClockLike {
abstract now(): string;
}
// An interface is erased by the compiler: at run time there is nothing to look up.
export interface ClockShape {
now(): string;
} TypeScript (main.ts) · traps/src/main.ts
import 'reflect-metadata';
import { Module, type Provider } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { Clock, ClockLike } from './clock.js';
import { AbstractClassService, ImportTypeService, ImportValueService, InterfaceService, UndecoratedService } from './services.js';
type Service = { name: string; prototype: { cutOff(): string } };
async function attempt(service: Service, providers: Provider[] = [Clock]) {
const types: Function[] | undefined = Reflect.getMetadata('design:paramtypes', service);
console.log(`${service.name}: design:paramtypes ${types ? `[${types.map((t) => t.name).join(', ')}]` : 'not recorded'}`);
@Module({ providers: [...providers, service as never] })
class TrialModule {}
try {
const app = await NestFactory.createApplicationContext(TrialModule, { logger: false, abortOnError: false });
try {
console.log(` cutOff(): ${app.get<{ cutOff(): string }>(service as never).cutOff()}`);
} catch (error) {
console.log(` Nest started, then cutOff() threw ${(error as Error).name}: ${(error as Error).message}`);
}
await app.close();
} catch (error) {
// The message, then the first two of the solutions Nest suggests (line 1 is the heading "Potential solutions:").
const lines = (error as Error).message.split('\n').filter((l) => l.trim());
console.log(` Nest refused to start: ${(error as Error).constructor.name}`);
for (const line of [lines[0], lines[2], lines[3]]) console.log(` ${line}`);
}
}
await attempt(ImportTypeService);
await attempt(ImportValueService);
await attempt(InterfaceService);
await attempt(AbstractClassService, [{ provide: ClockLike, useClass: Clock }]);
await attempt(UndecoratedService); import { session } from '../session.mjs';
// Five services that ask for a clock: three mistakes that erase the metadata, and two fixes.
session(`
npx tsc -p tsconfig.json
node dist/main.js
`); Output
$ npx tsc -p tsconfig.json $ node dist/main.js ImportTypeService: design:paramtypes [Function] Nest refused to start: UnknownDependenciesException Nest can't resolve dependencies of the ImportTypeService (?). Please make sure that the argument Function at index [0] is available in the TrialModule module. - Is TrialModule a valid NestJS module? - If Function is a provider, is it part of the current TrialModule? ImportValueService: design:paramtypes [Clock] cutOff(): orders close at 10:15 InterfaceService: design:paramtypes [Object] Nest refused to start: UnknownDependenciesException Nest can't resolve dependencies of the InterfaceService (?). Please make sure that the argument at index [0] is available in the current module. - The dependency at index [0] appears to be undefined at runtime - This commonly occurs when using 'import type' instead of 'import' for injectable classes AbstractClassService: design:paramtypes [ClockLike] cutOff(): orders close at 10:15 UndecoratedService: design:paramtypes not recorded Nest started, then cutOff() threw TypeError: Cannot read properties of undefined (reading 'now')
Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node run.mjs
| Mistake | What the compiler wrote | What Nest did | The fix |
|---|---|---|---|
import type { Clock } |
[Function] |
refused to start: no provider called Function | a normal import { Clock } |
| Parameter typed by an interface | [Object] |
refused to start, suggesting import type |
an abstract class, or @Inject() with a token |
No @Injectable() on the class |
nothing at all | started, then cutOff() threw a TypeError |
add @Injectable() |
Two lines of Nest’s messages are misleading, and worth remembering. For import type, Nest asks you to check
whether a provider named Function is in the module. For the interface, it suggests that you replaced import with
import type. In both cases the real cause is that the type had no run-time value. An interface can never be an
injection token: The key Nest's container looks up to find a provider: a class, a string or a symbol. An interface cannot be one, because it does not exist at run time.: use an abstract class, which exists at run time and can be
provided with useClass, or a string or symbol token with @Inject().
Cleaning up imports with “import type”
Linters and editors offer to turn imports that are only used as types into import type. A constructor parameter
of an injectable class looks like a type-only use, but the compiler needs the class itself for the metadata. Keep
import { Clock } for every class that a decorated constructor receives.
Checkpoint
Check: Mistakes that erase the metadata
TypeScript 6 or 7 for a Nest 12 project
npm’s latest TypeScript is 7.0.2, the new native compiler, while Nest 12’s tools stay on 6. The lesson’s
package.json installs both, 7.0.2 under the name typescript-7, so this session can compile kitchen.ts with
each and then ask each package what it exports:
// Asks each installed TypeScript what its main entry exports. Nest's CLI plugins and the schematics behind
// nest generate import the compiler API from 'typescript': createSourceFile, SyntaxKind and the rest.
import ts6 from 'typescript';
import * as ts7 from 'typescript-7';
console.log(`typescript ${ts6.version}: createSourceFile is a ${typeof ts6.createSourceFile}, SyntaxKind is an ${typeof ts6.SyntaxKind}`);
const names = Object.keys(ts7).filter((name) => name !== 'default' && name !== 'module.exports');
console.log(`typescript ${ts7.version}: its main entry exports only ${names.join(' and ')}`); import { session } from '../session.mjs';
// The lesson's package.json installs TypeScript 7.0.2 as "typescript-7" beside 6.0.3. Both compile the same file;
// then each is asked for the compiler API that Nest's tooling uses.
session(`
npx tsc --version
node ../node_modules/typescript-7/bin/tsc --version
npx tsc -p ../compiled/tsconfig.json --outDir dist-6
node ../node_modules/typescript-7/bin/tsc -p ../compiled/tsconfig.json --outDir dist-7
grep -A 1 'design:paramtypes' dist-6/kitchen.js dist-7/kitchen.js
node api.mjs
`); Output
$ npx tsc --version
Version 6.0.3
$ node ../node_modules/typescript-7/bin/tsc --version
Version 7.0.2
$ npx tsc -p ../compiled/tsconfig.json --outDir dist-6
$ node ../node_modules/typescript-7/bin/tsc -p ../compiled/tsconfig.json --outDir dist-7
$ grep -A 1 'design:paramtypes' dist-6/kitchen.js dist-7/kitchen.js
dist-6/kitchen.js: __metadata("design:paramtypes", [MenuService,
dist-6/kitchen.js- Clock])
--
dist-7/kitchen.js: __metadata("design:paramtypes", [MenuService,
dist-7/kitchen.js- Clock])
$ node api.mjs
typescript 6.0.3: createSourceFile is a function, SyntaxKind is an object
typescript 7.0.2: its main entry exports only version and versionMajorMinor
Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node run.mjs
Both compilers wrote the same design:paramtypes list, so an app compiled by either runs the same. The difference
is in the last two lines. Nest’s CLI plugins and the schematics behind nest generate call TypeScript’s compiler
API, createSourceFile, SyntaxKind and the rest, from the typescript package. TypeScript 6.0.3 exports it;
7.0.2’s main entry exports only its version. That is why @nestjs/cli 12.0.8 depends on typescript ~6.0.2, and
why the Swagger and GraphQL packages declare ^5.5.0 || ^6.0.0 as their peer.
Standard decorators are not an option yet
TypeScript 5.0 added the decorators of the TC39 proposal, which need no experimentalDecorators flag. Its release
notes state that they are not compatible with emitDecoratorMetadata and cannot decorate parameters, and Nest
needs both, for constructor injection and for @Param(), @Body() and their kind. Nest 12 projects keep
experimentalDecorators: true.
Keep a Nest 12 project on TypeScript 6, the version its package.json pins (^6.0.2 in the starter), and let a
newer major arrive through the CLI’s own dependency.
Checkpoint
Check: Which TypeScript for Nest 12
Practise: predict the metadata
Each question shows a constructor, an import or a build setting. Predict what the compiler writes, or what Nest does with it, and then read why:
Practice that adapts to you
Practise this lesson
10 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to mid · Warm-up
What is design:paramtypes, and who writes it and reads it in a Nest app?
It is decorator metadata: the list of a decorated class's constructor parameter types, kept as values that exist at run time. The TypeScript compiler writes it when emitDecoratorMetadata is on, as a __metadata("design:paramtypes", [MenuService, Clock]) entry in the class's __decorate call, and the reflect-metadata package stores it on the class when the file loads. Nest reads it at start-up with Reflect.getMetadata and injects one provider per entry. Interfaces become Object, an import type of a class becomes Function, and an undecorated class gets no list at all, which is why those three mistakes break injection.
-
Mid to senior
After a refactor, one Nest service fails at start-up because Nest cannot resolve "the argument Function at index [0]". After a build change, another service starts but its dependency is undefined at first use. Explain both failures, and how you would find the cause of each.
-
Mid to senior
Your team wants to drop the build step and run a NestJS 12 app with Node.js 24's built-in TypeScript support, to speed up development. Explain why it fails, and what you would offer instead.
Key takeaways
- A TypeScript compiler turns Nest’s decorators into
__decoratecalls; withemitDecoratorMetadatait also writes the constructor’s parameter types underdesign:paramtypes. - Nest reads that list at start-up to decide what to inject. Without it, Nest builds the class with no arguments and the error appears at first use.
- Node.js’s type stripping cannot run Nest source: parameter properties need a transform, decorators do not parse,
and Node.js never reads
tsconfig.json. import typeof a class, an interface as a parameter type and a missing@Injectable()all erase the metadata; use a normal import, an abstract class or a token, and the decorator.- TypeScript 7.0.2 writes the same metadata but has no compiler API in its main entry, so Nest 12’s tools stay on TypeScript 6.
Terms in this lesson
- decorator
- A function applied with @ to a class, method, property or parameter, which can record data about it or change it. Nest uses TypeScript's experimental decorators, which a compiler turns into function calls.
- decorator metadata
- Type information that TypeScript writes next to a decorated class when emitDecoratorMetadata is on, such as design:paramtypes. Libraries read it with the reflect-metadata package.
- design:paramtypes
- The metadata key under which TypeScript records the types of a decorated class's constructor parameters, as values that exist at run time: a class, or Object or Function when it cannot name one.
- injection token
- The key Nest's container looks up to find a provider: a class, a string or a symbol. An interface cannot be one, because it does not exist at run time.
- type stripping
- Running TypeScript by deleting its type annotations instead of compiling it, as Node.js does for .ts files. Syntax that needs a transformation, such as decorators or parameter properties, cannot run that way.
References
- TSConfig reference: experimentalDecorators (Microsoft (TypeScript))
- TSConfig reference: emitDecoratorMetadata (Microsoft (TypeScript))
- TypeScript 5.0 release notes: decorators (Microsoft (TypeScript))
- Node.js 24 documentation: Modules, TypeScript (OpenJS Foundation (Node.js))
- NestJS documentation: Providers (NestJS)
- NestJS documentation: Custom providers (non-class tokens) (NestJS)
- NestJS documentation: SWC (fast compiler) (NestJS)
- reflect-metadata on npm (npm)
- @nestjs/cli on npm (npm)
Report a problem with this lesson
Kept only in this browser. Your Learn progress