Historia zagadnienia:
W JavaScript obiekt Date używany jest do reprezentacji dat i czasu i znany jest ze swojej specyfiki działania — mutowalności, złożoności parsowania oraz szczególnych cech stref czasowych. TypeScript używa dla Date standardowych typów JS, jednak typizacja i praca z datą w ścisłym kodzie wymaga szczególnego podejścia.
Problem:
Date w JavaScript to mutowalny obiekt, który może generować nietypowe błędy (na przykład, setMonth() mutuje sam obiekt, co może prowadzić do niespodziewanego zachowania kodu). Ponadto integracja z zewnętrznymi bibliotekami (moment.js, date-fns, Day.js itp.) wymaga zachowania precyzji typów — na przykład moment.Instant lub specyficzne opakowania czasu, które nie są ze sobą kompatybilne. Czasami zwracany typ może być string lub number (timestamp), co prowadzi do błędów przy adnotowaniu typów lub używaniu API.
Rozwiązanie:
TypeScript typizuje nowy egzemplarz Date jako Date, a wszystkie jego metody są dostępne. Przy integracji z innymi bibliotekami ważne jest staranne monitorowanie typów wartości wejściowych i wyjściowych — używać jawnego rzutowania na Date (na przykład, new Date(value)), lub tworzyć niestandardowe typy-kontenery dla daty i czasu. Do konwersji między timestamp (number), ciągiem i obiektem Date należy ściśle określać typy i opisywać funkcje typami lub interfejsami.
Przykład kodu:
function toIsoString(d: Date | number | string): string { if (d instanceof Date) return d.toISOString(); if (typeof d === 'number' || typeof d === 'string') return new Date(d).toISOString(); throw new Error('Nieprawidłowa data'); }
Kluczowe cechy:
Czy typ "Date" w TypeScript jest prostym type value?
Nie, Date to obiekt, tzn. type reference. Porównywanie dat przez == lub === nie porównuje wartości, tylko referencje.
const d1 = new Date('2022-01-01'); const d2 = new Date('2022-01-01'); d1 === d2; // false
Czy można bezpośrednio przypisywać string lub liczbę do typu Date bez nowej inicjalizacji?
Nie, typ Date nie jest kompatybilny z number ani string. Należy tworzyć nowy egzemplarz: new Date(value).
const d: Date = new Date('2020-01-01'); // const d2: Date = '2020-01-01'; // Błąd typizacji
Czy funkcja przyjmująca Date będzie działać z obiektami zewnętrznych bibliotek (moment, dayjs)?
Nie, chyba że w bibliotece jawnie zaimplementowano rzutowanie/kompatybilność z Date przez valueOf/toDate, to są różne typy, a TypeScript nie rozpozna ich jako Date.
import dayjs from "dayjs"; function doSomething(d: Date) { /* ... */ } doSomething(dayjs()); // Błąd, ponieważ typ Dayjs nie jest kompatybilny z Date
W kodzie przyjmowano argument jako any, a następnie zakładano, że to Date. W praktyce API zwracał timestamp w stringu, co powodowało błąd przy wywołaniu .getFullYear().
Zalety:
Wady:
Programista stworzył jawny typ dla parametru (Date | string | number), wprowadził type guard i funkcja wywoływała new Date jawnie. Kompilator zmusza do jawnego przetwarzania wszystkich typów.
Zalety:
Wady: