Khi xây dựng các tính năng như CRUD (Create/Read/Update/Delete), việc tạo ra các biến thể khác nhau từ một entity type gốc thường rất hữu ích. Nest cung cấp một số hàm tiện ích (utility function) thực hiện việc chuyển đổi type để công việc này thuận tiện hơn.
Partial
Khi xây dựng các type dùng để kiểm tra dữ liệu đầu vào (validation, còn gọi là DTO), việc tạo ra biến thể create và update từ cùng một type thường rất hữu ích. Ví dụ, biến thể create có thể yêu cầu tất cả các field, còn biến thể update có thể cho phép tất cả field là optional (tùy chọn).
Nest cung cấp hàm tiện ích PartialType() để giúp việc này dễ hơn và giảm thiểu code khung (boilerplate).
Hàm PartialType() trả về một type (class) có tất cả property của input type được đặt thành optional. Ví dụ, giả sử ta có một type create như sau:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
Theo mặc định, tất cả các field này đều là bắt buộc. Để tạo một type có cùng các field nhưng mỗi field đều optional, hãy dùng PartialType() và truyền class reference (CreateCatDto) làm tham số:
export class UpdateCatDto extends PartialType(CreateCatDto) {}
Gợi ý Hàm PartialType() được import từ package @nestjs/swagger.
Pick
Hàm PickType() tạo ra một type (class) mới bằng cách chọn ra một tập property từ input type. Ví dụ, giả sử ta bắt đầu với một type như sau:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
Ta có thể chọn ra một tập property từ class này bằng hàm tiện ích PickType():
export class UpdateCatAgeDto extends PickType(CreateCatDto, ['age'] as const) {}
Gợi ý Hàm PickType() được import từ package @nestjs/swagger.
Omit
Hàm OmitType() tạo ra một type bằng cách lấy tất cả property từ input type rồi loại bỏ một tập key cụ thể. Ví dụ, giả sử ta bắt đầu với một type như sau:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
Ta có thể tạo ra một derived type có mọi property trừ name, như bên dưới. Trong cách viết này, tham số thứ hai truyền cho OmitType là một mảng tên property.
export class UpdateCatDto extends OmitType(CreateCatDto, ['name'] as const) {}
Gợi ý Hàm OmitType() được import từ package @nestjs/swagger.
Intersection
Hàm IntersectionType() kết hợp hai type thành một type (class) mới. Ví dụ, giả sử ta bắt đầu với hai type như sau:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
breed: string;
}
export class AdditionalCatInfo {
@ApiProperty()
color: string;
}
Ta có thể tạo ra một type mới kết hợp tất cả property của cả hai type.
export class UpdateCatDto extends IntersectionType(
CreateCatDto,
AdditionalCatInfo,
) {}
Gợi ý Hàm IntersectionType() được import từ package @nestjs/swagger.
Composition (kết hợp nhiều utility)
Các hàm tiện ích mapped type này có thể kết hợp (compose) được với nhau. Ví dụ, đoạn code sau sẽ tạo ra một type (class) có tất cả property của CreateCatDto trừ name, và các property đó sẽ được đặt thành optional:
export class UpdateCatDto extends PartialType(
OmitType(CreateCatDto, ['name'] as const),
) {}
Ghi chú người dịch Các hàm này chỉ hoạt động đúng khi class gốc dùng decorator @ApiProperty() (hoặc được CLI plugin tự động thêm) — chúng đọc metadata từ decorator, không suy ra từ kiểu TypeScript thuần. Nếu bạn quen với Go, hãy nghĩ nó gần giống việc tạo một struct mới bằng cách embedding một struct khác rồi override lại field, nhưng ở đây Nest làm việc đó tại runtime dựa trên reflection metadata thay vì compile-time.