Mapped Types

PartialType, PickType, OmitType, IntersectionType

Update DTOs usually have the same fields as create DTOs, but all optional. Instead of copying the class, use mapped types. They copy the validation decorators too.

Example

npm i @nestjs/mapped-types

Example output

added 1 package in 2s

src/products/dto/update-product.dto.ts

import { OmitType, PartialType, PickType, IntersectionType } from '@nestjs/mapped-types';
import { CreateProductDto } from './create-product.dto';

export class UpdateProductDto extends PartialType(CreateProductDto) {}           // all fields optional

export class UpdateStockDto extends PickType(CreateProductDto, ['stock'] as const) {}   // only stock

export class PublicProductDto extends OmitType(CreateProductDto, ['description'] as const) {}

class AuditDto { updatedBy: string; }
export class AdminUpdateProductDto extends IntersectionType(UpdateProductDto, AuditDto) {}

Note: If you use Swagger, import these helpers from @nestjs/swagger instead, so the API docs also know about the fields.

The Same Idea in TypeScript Types

Example

Mapped types are named after TypeScript utility types. The difference: TS utility types exist only at compile time, NestJS mapped types also create a real class with the validation rules.

interface CreateProduct {
  name: string;
  description?: string;
  price: number;
  stock: number;
  category: string;
}

type UpdateProduct = Partial<CreateProduct>;
type UpdateStock = Pick<CreateProduct, 'stock'>;
type PublicProduct = Omit<CreateProduct, 'description'>;

const patch: UpdateProduct = { price: 1199 };
const stock: UpdateStock = { stock: 50 };
const card: PublicProduct = { name: 'JBL Go 3', price: 2999, stock: 12, category: 'audio' };

console.log(patch, stock, card);

Output

{ price: 1199 } { stock: 50 } { name: 'JBL Go 3', price: 2999, stock: 12, category: 'audio' }