概述
在 NestJS 开发中,我们经常需要创建 DTO(Data Transfer Object)类。NestJS 提供了 @nestjs/mapped-types 包来帮助我们基于现有类创建新的 DTO 类,同时保留装饰器和验证元数据
TypeScript 内置类型工具 vs NestJS Mapped Types
1. PartialType vs Partial
TypeScript 内置 Partial
typescript
type Partial<T> = {
[P in keyof T]?: T[P];
};
// 使用示例
interface User {
name: string;
age: number;
}
type PartialUser = Partial<User>;
// 结果:{ name?: string; age?: number; }
特点:
- 仅用于类型检查,编译后不存在
- 无法保留装饰器和验证元数据
- 不能用于 class-validator
NestJS PartialType
typescript
import { PartialType } from '@nestjs/mapped-types';
class CreateUserDto {
@IsString()
name: string;
@IsNumber()
age: number;
}
class UpdateUserDto extends PartialType(CreateUserDto) {}
// 结果:一个类,保留了所有装饰器和验证元数据
特点:
- 运行时存在,是一个真正的类
- 保留装饰器和验证元数据
- 可用于 class-validator 和 NestJS DI
2. PickType vs Pick
TypeScript 内置 Pick
typescript
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
// 使用示例
type UserName = Pick<User, 'name'>;
// 结果:{ name: string; }
NestJS PickType
typescript
import { PickType } from '@nestjs/mapped-types';
class UserNameDto extends PickType(CreateUserDto, ['name'] as const) {}
// 结果:一个类,保留了 name 字段的装饰器
3. OmitType vs Omit
TypeScript 内置 Omit
typescript
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
// 使用示例
type UserWithoutAge = Omit<User, 'age'>;
// 结果:{ name: string; }
NestJS OmitType
typescript
import { OmitType } from '@nestjs/mapped-types';
class UserWithoutAgeDto extends OmitType(CreateUserDto, ['age'] as const) {}
// 结果:一个类,保留了剩余字段的装饰器
关键区别对比
| 特性 | TypeScript 内置类型 | NestJS mapped-types |
|---|---|---|
| 类型 | 类型别名(Type Alias) | 类(Class) |
| 运行时 | 不存在(编译后消失) | 存在(可在运行时使用) |
| 装饰器 | 无法保留 | 保留装饰器和验证元数据 |
| 验证 | 无法用于 class-validator | 可用于 class-validator |
| 依赖注入 | 无法用于 DI | 可用于 NestJS DI |
使用场景
TypeScript 内置类型(适合纯类型操作)
typescript
// 仅用于类型检查,不涉及运行时
function updateUser(id: string, data: Partial<User>) {
// ...
}
NestJS mapped-types(适合 DTO 和验证)
typescript
import { Controller, Post, Patch, Body } from '@nestjs/common';
import { PartialType } from '@nestjs/mapped-types';
@Controller('users')
export class UserController {
@Post()
create(@Body() dto: CreateUserDto) {
// class-validator 会自动验证
}
@Patch(':id')
update(@Body() dto: UpdateUserDto) {
// UpdateUserDto 继承了 CreateUserDto 的所有验证规则
}
}
最佳实践
- DTO 类使用 mapped-types :当需要创建可验证的 DTO 类时,使用
@nestjs/mapped-types - 纯类型操作使用内置类型:当只需要类型层面的操作时,使用 TypeScript 内置类型
- 保持一致性:在同一个项目中,保持使用方式的一致性