Chuyển đến nội dung chính

第 2 課:NestJS 的 TypeScript 基礎知識

查看 NestJS 的基本 TypeScript:裝飾器、泛型、介面、枚舉、型別防護、實用程式類型。為 NestJS 專案配置 tsconfig.json。

💻 程式設計 — 第 2 課 第 2 課:NestJS 的 TypeScript 基礎知識

NestJS:從基礎到高級

第 1 部分:NestJS 平台

亞洲開發網

1. 為什麼 TypeScript 對 NestJS 很重要?

NestJS 完全使用 TypeScript 構建,並充分利用 TypeScript 的高級功能,例如 裝飾器, 元數據反映, 泛型。了解 TypeScript 將幫助您更有效地使用 NestJS。

2. 裝飾器-NestJS的核心

裝飾器是 NestJS 最常用的 TypeScript 功能。他們是 特殊功能 附加到類別、方法、屬性或參數以新增元資料。

類別裝飾器

// Class Decorator
function Controller(prefix: string) {
  return function (target: Function) {
    Reflect.defineMetadata('prefix', prefix, target);
  };
}

@Controller('/users')
class UsersController {
  // NestJS tự biết controller này handle route /users
}

方法裝飾器

// Method Decorator
function Log(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${key} with args:`, args);
    const result = original.apply(this, args);
    console.log(`Result:`, result);
    return result;
  };
}

class UserService {
  @Log
  findUser(id: number) {
    return { id, name: 'John' };
  }
}

參數裝飾器

// Trong NestJS, bạn sẽ gặp:
@Get(':id')
findOne(
  @Param('id') id: string,           // Parameter decorator
  @Query('include') include: string,  // Parameter decorator
  @Body() body: CreateUserDto,        // Parameter decorator
) {
  // ...
}

裝飾器組合

// Kết hợp nhiều decorators
function Auth(...roles: string[]) {
  return applyDecorators(
    UseGuards(AuthGuard('jwt'), RolesGuard),
    Roles(...roles),
    ApiBearerAuth(),
  );
}

@Auth('admin')
@Get('admin/dashboard')
getDashboard() { ... }

3. 介面和型別別名

// Interface — mở rộng được, dùng cho object shapes
interface User {
  id: number;
  name: string;
  email: string;
  role: UserRole;
  createdAt: Date;
}

// Extending interfaces
interface AdminUser extends User {
  permissions: string[];
  lastLogin: Date;
}

// Type alias — linh hoạt hơn, dùng cho unions, tuples
type UserRole = 'admin' | 'editor' | 'viewer';
type ApiResponse<T> = {
  data: T;
  meta: { total: number; page: number };
};

// Trong NestJS, dùng interface cho DTOs shape, class cho validation

4. 泛型-程式碼重用

// Generic function
function wrapResponse<T>(data: T): ApiResponse<T> {
  return { data, meta: { total: 1, page: 1 } };
}

// Generic class — pattern phổ biến trong NestJS Repository
class BaseRepository<T> {
  private items: T[] = [];

  findAll(): T[] {
    return this.items;
  }

  findById(id: number): T | undefined {
    return this.items.find((item: any) => item.id === id);
  }

  create(item: T): T {
    this.items.push(item);
    return item;
  }
}

// Sử dụng
class UserRepository extends BaseRepository<User> {
  findByEmail(email: string): User | undefined {
    return this.findAll().find(u => u.email === email);
  }
}

// Generic constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

5. 枚舉

// String enum — dùng nhiều trong NestJS
enum UserRole {
  Admin = 'ADMIN',
  Editor = 'EDITOR', 
  Viewer = 'VIEWER',
}

// Const enum — tối ưu performance
const enum HttpStatus {
  OK = 200,
  Created = 201,
  BadRequest = 400,
  Unauthorized = 401,
  NotFound = 404,
}

// Sử dụng trong NestJS
@Roles(UserRole.Admin)
@Get('admin')
adminOnly() { ... }

6. 常用實用程式類型

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  avatar?: string;
}

// Partial — tất cả fields optional (dùng cho Update DTO)
type UpdateUserDto = Partial<User>;

// Pick — chọn một số fields
type UserPublicInfo = Pick<User, 'id' | 'name' | 'avatar'>;

// Omit — loại bỏ fields (ẩn password)
type UserResponse = Omit<User, 'password'>;

// Record — key-value mapping
type UserPermissions = Record<string, boolean>;

// Required — tất cả fields bắt buộc
type StrictUser = Required<User>;

// NestJS PartialType, PickType, OmitType từ @nestjs/mapped-types
// làm tương tự nhưng giữ validation decorators
import { PartialType, OmitType } from '@nestjs/mapped-types';
class UpdateUserDto extends PartialType(CreateUserDto) {}
class UserResponseDto extends OmitType(CreateUserDto, ['password']) {}

7. 類型保護與縮小

// typeof guard
function processInput(input: string | number) {
  if (typeof input === 'string') {
    return input.toUpperCase(); // TypeScript biết đây là string
  }
  return input.toFixed(2); // TypeScript biết đây là number
}

// instanceof guard
class HttpException {
  constructor(public message: string, public status: number) {}
}

class NotFoundException extends HttpException {
  constructor(resource: string) {
    super(`${resource} not found`, 404);
  }
}

function handleError(error: Error | HttpException) {
  if (error instanceof HttpException) {
    return { statusCode: error.status, message: error.message };
  }
  return { statusCode: 500, message: 'Internal Server Error' };
}

// Custom type guard
interface AdminUser { role: 'admin'; permissions: string[] }
interface RegularUser { role: 'user'; subscription: string }

function isAdmin(user: AdminUser | RegularUser): user is AdminUser {
  return user.role === 'admin';
}

8.為NestJS配置tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "ES2021",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": true,
    "noImplicitAny": true,
    "strictBindCallApply": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "paths": {
      "@/*": ["src/*"],
      "@modules/*": ["src/modules/*"],
      "@common/*": ["src/common/*"]
    }
  }
}

NestJS 的兩個最重要的選項:

  • emitDecoratorMetadata:true — 允許 TypeScript 為裝飾器發出元資料(NestJS DI 需要這個)
  • 實驗裝飾器:true — 啟用裝飾器

9. 總結

在本文中,您了解了 NestJS 所需的核心 TypeScript 功能:

  • 裝飾器:類別、方法、參數裝飾器——NestJS 的基礎
  • 介面和類型:定義資料的形狀
  • 泛型:建立可重複使用的、型別安全的程式碼
  • 列舉:常量的定義
  • 實用程式類型:DTO 的部分、選取、省略
  • 類型保護裝置:縮小類型是安全的

下一課,我們將安裝 NestJS CLI 並創建我們的第一個專案!