Как правильно указать тип для даты (Date) в TypeScript? Проблемы с типами дат
Основное решение: используйте встроенный тип Date
В TypeScript для работы с датами существует встроенный тип Date. Он полностью соответствует нативному JavaScript-объекту Date и предоставляет все его методы с правильной типизацией.
function formatDate(date: Date): string {
return date.toLocaleDateString('ru-RU');
}
// Использование:
const today = new Date();
console.log(formatDate(today)); // Работает
Как работать со строками, которые представляют даты
Если вы получаете дату как строку (например, из HTML-инпута), её нужно явно преобразовать в объект Date. TypeScript не делает это автоматически.
// Получаем строку из инпута
const dateString: string = document.querySelector('input[type="date"]').value;
// dateString имеет тип string, а не Date
// Преобразуем в Date
const date: Date = new Date(dateString);
// Теперь можно передавать в функцию
console.log(formatDate(date));
Важно: конструктор new Date(str) может вернуть Invalid Date, если строка имеет неправильный формат. TypeScript не предупредит об этом на этапе компиляции.
Обработка null, undefined и невалидных дат
Если функция может вернуть или принять null / undefined, используйте union-типы:
function formatDateSafe(date: Date | null | undefined): string {
if (!date) {
return 'Дата не указана';
}
return date.toLocaleDateString('ru-RU');
}
// Или более строгий вариант с проверкой на Invalid Date
function formatDateStrict(date: Date | string | null): string {
if (!date) {
return 'Дата не указана';
}
const parsedDate = typeof date === 'string' ? new Date(date) : date;
// Проверка на Invalid Date (NaN timestamp)
if (isNaN(parsedDate.getTime())) {
return 'Некорректная дата';
}
return parsedDate.toLocaleDateString('ru-RU');
}
Рекомендации по типизации
- Для параметров функций: используйте
DateилиDate | null - Для возвращаемых значений:
Date,Date | nullилиDate | undefined - Для строковых дат из внешних источников (API, формы): принимайте как
stringи конвертируйте внутри функции - Избегайте
any: он отключает проверку типов и может привести к ошибкам в рантайме
Продвинутый подход: кастомные типы
Если вы хотите быть более строгими, можно создать брендированный тип:
type ISO8601Date = string & { __brand: 'ISO8601' };
function parseISO(dateStr: ISO8601Date): Date {
return new Date(dateStr);
}
// Но это уже избыточно для большинства проектов
Что НЕ нужно делать
// ❌ Плохо: теряем информацию о типе
function formatDate(date: any) { ... }
// ❌ Плохо: неправильный тип
function formatDate(date: string) { ... } // У строки нет toLocaleDateString
// ✅ Хорошо: явно указываем Date
function formatDate(date: Date) { ... }
<<Альтернативный подход: использование библиотек для работы с датами
В реальных проектах часто используют специализированные библиотеки, такие как date-fns или Luxon, которые предоставляют более удобные типы и функции.
Пример с date-fns
import { format, parseISO } from 'date-fns';
function formatDate(date: Date | string): string {
const parsedDate = typeof date === 'string' ? parseISO(date) : date;
return format(parsedDate, 'dd.MM.yyyy');
}
// date-fns автоматически обрабатывает невалидные даты
Пример с Luxon
import { DateTime } from 'luxon';
function formatDate(dateStr: string): string {
const date = DateTime.fromISO(dateStr);
if (!date.isValid) {
return 'Некорректная дата';
}
return date.toFormat('dd.MM.yyyy');
}
Преимущества библиотек:
- Встроенная валидация дат
- Более удобные методы форматирования
- Поддержка часовых поясов
- Хорошая TypeScript-типизация "из коробки"
Недостатки:
- Увеличение размера бандла (хотя date-fns поддерживает tree-shaking)
- Дополнительная зависимость в проекте
Для небольших проектов или простых задач достаточно встроенного типа Date. Для сложной работы с датами (часовые пояса, сложное форматирование) лучше использовать специализированные библиотеки.