Avatar ·

Как правильно указать тип для даты (Date) в TypeScript? Проблемы с типами дат

Всем привет! Я только начал изучать TypeScript и столкнулся с проблемой при работе с датами. У меня есть функция, которая принимает дату и возвращает её отформатированную строку: ```typescript function formatDate(date: ???) { return date.toLocaleDateString('ru-RU'); } ``` Я не могу понять, какой тип нужно указать вместо `???`. Пробовал `string`, но тогда TypeScript ругается, что у строки нет метода `toLocaleDateString`. Пробовал `any` — работает, но это плохая практика, как я понял. Ещё видел в коде коллег, что они используют `Date`, но при попытке передать строку вида `"2024-01-15"` возникает ошибка. Вторая проблема: я получаю дату из HTML-инпута (через `document.querySelector('input[type="date"]').value`), и она приходит как строка. Как мне правильно преобразовать её в тип Date и убедиться, что TypeScript не выдаёт ошибок? И ещё: я заметил, что если я создаю новую дату через `new Date()`, то TypeScript автоматически выводит тип `Date`. Но если я пишу функцию, которая возвращает дату, какой тип возврата указать? И что делать, если функция может вернуть `null` или `undefined`? Буду благодарен за объяснение с примерами. Заранее спасибо! <<>>

Основное решение: используйте встроенный тип 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. Для сложной работы с датами (часовые пояса, сложное форматирование) лучше использовать специализированные библиотеки.