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

Bài 2: TypeScript Essentials cho NestJS

Ôn tập TypeScript cần thiết cho NestJS: Decorators, Generics, Interfaces, Enums, Type Guards, Utility Types. Cấu hình tsconfig.json cho NestJS project.

💻 Lập trình — Bài 2 Bài 2: TypeScript Essentials cho NestJS

NestJS: Từ Cơ bản đến Nâng cao

Phần 1: Nền tảng NestJS

xdev.asia

1. Tại sao TypeScript quan trọng với NestJS?

NestJS được xây dựng hoàn toàn bằng TypeScript và tận dụng tối đa các tính năng nâng cao của TypeScript như Decorators, Metadata Reflection, Generics. Hiểu rõ TypeScript sẽ giúp bạn làm việc hiệu quả hơn với NestJS.

2. Decorators — Trái tim của NestJS

Decorators là tính năng TypeScript được NestJS sử dụng nhiều nhất. Chúng là các hàm đặc biệt gắn vào class, method, property hoặc parameter để bổ sung metadata.

Class Decorators

// 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 Decorators

// 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' };
  }
}

Parameter Decorators

// 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
) {
  // ...
}

Decorator Composition

// 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. Interfaces & Type Aliases

// 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. Generics — Code tái sử dụng

// 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. Enums

// 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. Utility Types hay dùng

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. Type Guards & Narrowing

// 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. Cấu hình tsconfig.json cho NestJS

{
  "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/*"]
    }
  }
}

Hai options quan trọng nhất cho NestJS:

  • emitDecoratorMetadata: true — Cho phép TypeScript emit metadata cho decorators (NestJS DI cần cái này)
  • experimentalDecorators: true — Bật tính năng decorators

9. Tổng kết

Trong bài này, bạn đã nắm được các tính năng TypeScript cốt lõi cần cho NestJS:

  • Decorators: Class, Method, Parameter decorators — nền tảng của NestJS
  • Interfaces & Types: Định nghĩa shape cho data
  • Generics: Tạo code tái sử dụng, type-safe
  • Enums: Định nghĩa constants
  • Utility Types: Partial, Pick, Omit cho DTOs
  • Type Guards: Narrowing types an toàn

Bài tiếp theo, chúng ta sẽ cài đặt NestJS CLI và khởi tạo project đầu tiên!