Tsconfig.json – file quyết định TypeScript sẽ build code của bạn như thế nào
Tác giả: Flop Dev
Đăng nhập để đánh giá bài viết

Hiểu tsconfig qua NestJS — khi config không còn là “file cho có”
Bạn chạy:
nest new my-appNest CLI tạo ra một file:
tsconfig.json
Bạn nhìn vào… và:
Ừm… để đó
Copy sang project khác
Không động vào nữa
👉 Và đó là sai lầm.
Sự thật ít người nói
tsconfig.jsonkhông phải config cho TypeScript.
Nó là hợp đồng giữa compile-time và runtime.
Nó quyết định:
Code của bạn được biến thành JS như thế nào
Nest có inject dependency được không
Decorator có thực sự hoạt động không
Và… app có crash hay không
Bóc tách file tsconfig của Nest
File bạn đang dùng:
{
"module": "commonjs",
"target": "ES2023",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"strictNullChecks": true,
"incremental": true
}Nhìn nhiều, nhưng thực ra chia thành 4 nhóm:
1. Runtime shape (JS sẽ trông như nào)
"module": "commonjs",
"target": "ES2023"👉 Đây là quyết định:
Node sẽ hiểu code của bạn ra sao
Có cần transpile xuống ES5 không
Có dùng
requirehayimport
2. Type system (TS kiểm soát bạn thế nào)
"strictNullChecks": true👉 Một dòng nhưng cứu bạn khỏi hàng đống bug:
const user: string = null // ❌
Nếu tắt:


user.length // 💥 runtime crash
3. Performance
"incremental": true👉 TypeScript sẽ cache build → nhanh hơn đáng kể khi project lớn
4. Quan trọng nhất: Decorator system
"experimentalDecorators": true,
"emitDecoratorMetadata": true👉 Đây là lý do NestJS tồn tại.
Cùng một đoạn code — nhưng 3 thế giới runtime khác nhau
Case: app.controller.ts (Nest generate)
import { Controller, Get } from '@nestjs/common';
@Controller()
export class AppController {
@Get()
getHello(): string {
return 'Hello World';
}
}Trạng thái 1: FULL ON (Nest hoạt động đúng)
{
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}JS sau khi build
let AppController = class AppController {
getHello() {
return 'Hello World';
}
};
__decorate([
(0, common_1.Get)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", String)
], AppController.prototype, "getHello", null);
AppController = __decorate([
(0, common_1.Controller)()
], AppController);

Điều quan trọng
__metadata("design:paramtypes", [])
__metadata("design:returntype", String)👉 Đây là thứ Nest sẽ đọc ở runtime.
Runtime thực sự
Reflect.getMetadata("design:paramtypes", AppController)
Reflect.getMetadata("design:returntype", AppController.prototype, "getHello")👉 Nest biết:
Method này return gì
Có param gì
Inject gì
Kết luận trạng thái này
👉 Mọi thứ hoạt động:

Service được tạo và tiêm đúng vào nơi cần
Controller nhận đúng instance
Method gọi được getHello bên trong service
Không có undefined / không có lỗi runtime
Trạng thái 2: Decorator có — Metadata mất
{
"experimentalDecorators": true,
"emitDecoratorMetadata": false
}JS sau khi build
let AppController = class AppController {
getHello() {
return 'Hello World';
}
};
__decorate([
(0, common_1.Get)()
], AppController.prototype, "getHello", null);
AppController = __decorate([
(0, common_1.Controller)()
], AppController);
So sánh nhanh
__metadata(...)❌ không có
Reflect đọc được type❌ undefined
Điều gì xảy ra?
Controller này:
👉 vẫn chạy được ❗
Vì:
@Controller,@Getchỉ cần decoratorKhông cần type metadata
Nhưng với Dependency Injection
constructor(private service: AppService) {}👉 Nest sẽ làm:
Reflect.getMetadata("design:paramtypes", AppController)👉 Và nhận:
undefinedKết quả

Nest can't resolve dependencies of the AppController (?)Quan trọng: Service không phải “chạy sai” — nó không được tạo
Đây là điểm nhiều người hiểu sai:
❌ Không phải:
Service chạy lỗi
Inject sai
Method bị undefined
✅ Mà là:
Instance của service KHÔNG BAO GIỜ tồn tại
Trạng thái 3: Tắt cả decorator
{
"experimentalDecorators": false,
"emitDecoratorMetadata": false
}Compile sẽ fail ngay
Decorators are not valid here.

👉 TypeScript chặn từ compile-time
Insight
experimentalDecorators - Cho phép syntax
@emitDecoratorMetadata - Cho phép runtime hiểu metadata
👉 Thiếu cái đầu:
❌ Không compile được
👉 Thiếu cái sau:
✔ Compile OK
❌ Runtime sai
Nhưng… metadata này thực chất là gì?
Nó từ đâu ra?
Ai tạo ra?
Tại sao TypeScript lại biết được?
👉 Và quan trọng hơn:
Làm sao tự tạo metadata của riêng bạn?
➡️ Phần tiếp theo:
Giải phẫu
reflect-metadatavà cơ chế phía sau NestJS DI

Flop Dev
Editor tại kilobai.com