Dates in TypeScript: How to Properly Declare a Type and Not Lose Your Mind

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why is "Date" in TypeScript a Trap?

\n

Imagine: you're writing an application, everything works perfectly. But one fine day, a bug report comes in: "A user from Tokyo sees the date one day earlier." You open the code and see the classic: let date: Date = "2024-12-01". Or even worse — any. Sound familiar?

\n

The question "How do I express a date type in TypeScript?" on Stack Overflow has garnered tens of thousands of views. And for good reason. It seems the answer is simple: use Date. But in practice, it's more complicated. TypeScript is a superset of JavaScript, and Date in JS is an object that behaves... peculiarly.

\n

In this article, we'll break down not just the syntax, but proper date typing that will save your project from bugs, improve performance, and make the code understandable for your team. You'll learn how to work with real dates in TypeScript without wasting time on debugging.

\n\n

1. Basic Typing: Date, string, or number?

\n

The most obvious option is the built-in Date type:

\n
let birthday: Date = new Date('1990-05-15');
\n

Does it work? Yes. But there's a catch: TypeScript does not check if the date is valid. You can write new Date('abc') — there will be no compilation error, but at runtime you'll get Invalid Date. And trying to call .getTime() will return NaN.

\n

A stricter option is to use ISO 8601 formatted strings:

\n
type ISODateString = string;\nlet eventDate: ISODateString = '2024-12-01T10:00:00Z';
\n

Pros: simple type, easily serializable to JSON. Cons: no validation — you can assign 'hello', and TS will stay silent.

\n

The third option is timestamp (number):

\n
type Timestamp = number;\nlet ms: Timestamp = Date.now();
\n

Ideal for mathematical operations, but unreadable for humans. And again — any number will pass the check.

\n\n

Summary of Basic Types

\n

If you need quick and dirty typing — use Date. If you're working with an API — use string. If it's only calculations — use number. But for serious projects, this is not enough. Let's dive deeper.

\n\n

2. Custom Types and Branded Types

\n

To prevent TypeScript from confusing a date-string with an ordinary string, branded types are used. It's like a sticker saying "I'm a date, verified!":

\n
type DateString = string & { __brand: 'DateString' };\n\nfunction toDateString(date: Date): DateString {\n    return date.toISOString() as DateString;\n}\n\nfunction processDate(d: DateString) {\n    // here d is definitely an ISO string\n}\n\nconst iso = toDateString(new Date());\nprocessDate(iso); // OK\n// processDate('hello'); // ❌ Error!
\n

This adds compile-time safety. The cost is a bit more code. But for large projects (especially if you have microservices or REST APIs), this is the gold standard.

\n\n

3. Libraries for Working with Dates in TypeScript

\n

Raw Date is evil. It's mutable, parses strings inconsistently, and timezones are a separate pain. Therefore, real projects use libraries. Here are three favorites:

\n\n

3.1. date-fns (Lightweight and Modular)

\n
import { format, parseISO } from 'date-fns';\n\nconst date: Date = parseISO('2024-12-01T10:00:00Z');\nconst formatted: string = format(date, 'dd.MM.yyyy');
\n

Pros: tree-shaking, immutable (returns new dates), excellent typing.

\n\n

3.2. Day.js (Minimalist)

\n
import dayjs, { Dayjs } from 'dayjs';\n\nconst now: Dayjs = dayjs();\nconst formatted: string = now.format('DD.MM.YYYY');
\n

Day.js is like Moment.js, but without the heavy bundle. The Dayjs type is a wrapper that is easy to pass between functions.

\n\n

3.3. Luxon (

Blogs

Book Recommendations