Декоратор @IsDate из библиотеки class-validator
предназначен для проверки того, что значение является объектом типа
Date. Проверка выполняется строго: значение должно быть
экземпляром Date, а не строкой, числом или другим
представлением даты.
import { IsDate } from 'class-validator'
class UserDto {
@IsDate()
createdAt: Date
}
@IsDateДекоратор валидирует:
Dateinstanceof DateУспешно проходят проверку:
new Date()
new Date('2025-01-01')
Не проходят проверку:
'2025-01-01'
1715452512
true
{}
[]
null
undefined
import { validate } from 'class-validator'
import { IsDate } from 'class-validator'
class EventDto {
@IsDate()
eventDate: Date
}
async function run() {
const dto = new EventDto()
dto.eventDate = new Date()
const errors = await validate(dto)
console.log(errors)
}
run()
Результат:
[]
Одна из самых распространённых ошибок — передача даты в виде строки.
class EventDto {
@IsDate()
eventDate: Date
}
const dto = new EventDto()
dto.eventDate = '2025-05-10' as any
Результат валидации:
[
ValidationError {
property: 'eventDate',
constraints: {
isDate: 'eventDate must be a Date instance'
}
}
]
При получении данных из HTTP-запроса дата почти всегда приходит строкой:
{
"eventDate": "2025-05-10T12:00:00.000Z"
}
После JSON.parse() значение остаётся строкой:
typeof body.eventDate // string
Из-за этого @IsDate() выдаёт ошибку.
DateДля корректной работы обычно используется библиотека class-transformer.
import { Type } from 'class-transformer'
import { IsDate } from 'class-validator'
class EventDto {
@Type(() => Date)
@IsDate()
eventDate: Date
}
Теперь строка автоматически преобразуется в объект
Date.
plainToInstanceimport 'reflect-metadata'
import { plainToInstance } from 'class-transformer'
import { validate } from 'class-validator'
class EventDto {
@Type(() => Date)
@IsDate()
eventDate: Date
}
async function run() {
const body = {
eventDate: '2025-05-10T12:00:00.000Z'
}
const dto = plainToInstance(EventDto, body)
console.log(dto.eventDate instanceof Date)
const errors = await validate(dto)
console.log(errors)
}
run()
Результат:
true
[]
JavaScript позволяет создать некорректный объект
Date.
const date = new Date('invalid')
Результат:
Invalid Date
Однако объект всё равно остаётся экземпляром Date.
date instanceof Date // true
Из-за этого возникает важная особенность:
@IsDate()
date: Date
Такой декоратор может пропустить Invalid Date.
Для полной проверки обычно комбинируют @IsDate и
@MinDate / @MaxDate, либо создают собственный
валидатор.
Пример проверки через isNaN:
function isValidDate(date: Date) {
return !isNaN(date.getTime())
}
@MinDateimport { IsDate, MinDate } from 'class-validator'
class EventDto {
@IsDate()
@MinDate(new Date())
eventDate: Date
}
Поле должно:
Date@MaxDateimport { IsDate, MaxDate } from 'class-validator'
class ArchiveDto {
@IsDate()
@MaxDate(new Date())
createdAt: Date
}
Дата не может быть из будущего.
class PromotionDto {
@IsDate()
@MinDate(new Date('2025-01-01'))
@MaxDate(new Date('2025-12-31'))
activeUntil: Date
}
class EventDto {
@IsDate({
message: 'Дата события имеет неверный формат'
})
eventDate: Date
}
class EventDto {
@IsDate({
message: args => {
return `Поле ${args.property} должно быть объектом Date`
}
})
eventDate: Date
}
Для необязательных свойств используется @IsOptional.
import { IsOptional, IsDate } from 'class-validator'
class UpdateUserDto {
@IsOptional()
@IsDate()
birthday?: Date
}
Поведение:
| Значение | Результат |
|---|---|
undefined |
проходит |
null |
проходит |
new Date() |
проходит |
'2025-01-01' |
ошибка |
@IsDate и @IsDateString@IsDateПроверяет объект Date.
@IsDate()
createdAt: Date
@IsDateStringПроверяет строку формата ISO 8601.
@IsDateString()
createdAt: string
@IsDateПодходит для:
Date@IsDateStringПодходит для:
{
"createdAt": "2025-05-10T10:00:00.000Z"
}
class RequestDto {
@IsDateString()
createdAt: string
}
class RequestDto {
@Type(() => Date)
@IsDate()
createdAt: Date
}
В NestJS декоратор особенно часто применяется вместе с
ValidationPipe.
@Controller('events')
export class EventsController {
@Post()
create(@Body() dto: CreateEventDto) {
return dto
}
}
DTO:
import { Type } from 'class-transformer'
import { IsDate } from 'class-validator'
export class CreateEventDto {
@Type(() => Date)
@IsDate()
startDate: Date
}
Глобальный pipeline:
app.useGlobalPipes(
new ValidationPipe({
transform: true
})
)
transform: trueБез трансформации:
startDate = '2025-05-10'
Тип:
string
С трансформацией:
startDate = new Date('2025-05-10')
Тип:
Date
import { IsArray, IsDate } from 'class-validator'
import { Type } from 'class-transformer'
class ScheduleDto {
@IsArray()
@Type(() => Date)
@IsDate({ each: true })
dates: Date[]
}
each: trueОпция заставляет валидатор проверять каждый элемент массива отдельно.
dates = [
new Date(),
new Date(),
new Date()
]
Если один элемент невалиден:
dates = [
new Date(),
'invalid'
]
валидация завершится ошибкой.
class PeriodDto {
@Type(() => Date)
@IsDate()
start: Date
@Type(() => Date)
@IsDate()
end: Date
}
ValidateNestedimport { ValidateNested } from 'class-validator'
import { Type } from 'class-transformer'
class EventDto {
@ValidateNested()
@Type(() => PeriodDto)
period: PeriodDto
}
@Type(() => Date)@IsDate()
createdAt: Date
Из JSON всегда придёт строка.
transform: trueВ NestJS без трансформации данные не будут преобразованы автоматически.
@IsDate для строкиНеправильно:
@IsDate()
createdAt: string
Правильно:
@IsDateString()
createdAt: string
или
@Type(() => Date)
@IsDate()
createdAt: Date
class UserDto {
@IsOptional()
@Type(() => Date)
@IsDate()
deletedAt: Date | null
}
import {
ValidatorConstraint,
ValidatorConstraintInterface,
ValidationArguments
} from 'class-validator'
@ValidatorConstraint({ name: 'isRealDate' })
export class IsRealDateConstraint
implements ValidatorConstraintInterface {
validate(value: any) {
return (
value instanceof Date &&
!isNaN(value.getTime())
)
}
defaultMessage(args: ValidationArguments) {
return `${args.property} содержит некорректную дату`
}
}
Использование:
import { Validate } from 'class-validator'
class EventDto {
@Validate(IsRealDateConstraint)
eventDate: Date
}
class MeetingDto {
@IsDate()
@MinDate(new Date())
meetingDate: Date
}
Недостаток такого подхода — дата вычисляется один раз при запуске приложения.
Для проверки относительно текущего момента лучше использовать кастомный валидатор.
validate(value: Date) {
return value.getTime() > Date.now()
}
Date в JavaScript хранит дату в UTC, но отображение
зависит от локальной временной зоны.
new Date('2025-05-10')
Может отображаться по-разному на разных серверах.
Наиболее безопасный формат:
2025-05-10T12:00:00.000Z
Он:
В Swagger:
@ApiProperty({
type: String,
format: 'date-time'
})
@Type(() => Date)
@IsDate()
createdAt: Date
@IsDate работает очень быстро, так как использует
простую проверку:
value instanceof Date
Даже при большом количестве DTO влияние на производительность минимально.
Упрощённая логика:
function isDate(value: unknown): boolean {
return value instanceof Date
}
import {
IsDate,
IsOptional,
MinDate,
MaxDate
} from 'class-validator'
import { Type } from 'class-transformer'
export class CreateBookingDto {
@Type(() => Date)
@IsDate()
@MinDate(new Date())
checkIn: Date
@Type(() => Date)
@IsDate()
checkOut: Date
@IsOptional()
@Type(() => Date)
@IsDate()
cancelledAt?: Date
}
const dto = plainToInstance(CreateBookingDto, body)
console.log(dto.checkIn)
console.log(typeof dto.checkIn)
console.log(dto.checkIn instanceof Date)
@IsDate особенно полезен в связке с:
emitDecoratorMetadatareflect-metadata{
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
Обычно импортируется в точке входа приложения:
import 'reflect-metadata'
undefinedclass TestDto {
@IsDate()
value: Date
}
value = undefined
Результат:
ошибка валидации
Для разрешения undefined нужен
@IsOptional().
nullvalue = null
Результат:
ошибка валидации
При использовании @IsOptional():
@IsOptional()
@IsDate()
value?: Date | null
null будет разрешён.
@IsDateДекоратор решает несколько задач одновременно: