У JSON немає типу «дата». Laravel серіалізує дати моделей як рядки ISO 8601 ("2026-10-04T07:00:00.000000Z"), і JSON.parse повертає саме рядок. Тип Date в інтерфейсі нічого не перетворює - це лише твердження розробника.
interface Order {
id: number;
createdAt: Date; // неправда: під час виконання тут рядок
}
const order: Order = await response.json();
order.createdAt.getFullYear(); // TypeError: getFullYear is not a function
TypeScript помилки не покаже - відповідь response.json() має тип any.
Варіант 1 - чесний тип: описати те, що справді приходить, і перетворювати там, де потрібно:
interface Order {
id: number;
createdAt: string; // ISO 8601
}
const created = new Date(order.createdAt);
Варіант 2 - перетворення на межі через схему:
import * as z from 'zod';
const OrderSchema = z.object({
id: z.number(),
createdAt: z.iso.datetime({ offset: true }).transform((value) => new Date(value)),
});
type Order = z.infer<typeof OrderSchema>; // createdAt: Date - тепер це правда
const order = OrderSchema.parse(await response.json());
Рядок перевіряється на формат і перетворюється на Date; далі весь код працює з датою.
Нюанси з датами:
- дата без часу (
"2026-10-04") уnew Date()розбирається як північ UTC - у Києві це ще 4 жовтня, а в Нью-Йорку вже 3-тє. Для дат без часу (день народження, дата події) краще лишати рядок або використовуватиTemporal.PlainDate; - дата з поясом (
...Zчи+03:00) - однозначна мить, її безпечно перетворювати; - у зворотному напрямку
JSON.stringify(new Date())дає рядок ISO в UTC - Laravel його правильно розбере.
Те саме стосується інших типів, яких немає в JSON: BigInt (великі id - краще рядками), Map/Set, undefined (зникає), гроші (decimal з Laravel часто приходить рядком "125.50" - і це правильно, щоб не втратити точність).
Загальне правило: тип даних з API описує формат JSON, а не бажану модель. Перетворення - явний крок у API-клієнті.