DocsType Safety

TypeScript Ergonomics & Branded Types

DaySafe is written in TypeScript from the ground up, featuring branded nominal types, strict type guards, and zero-overhead compile-time guarantees.

The DateOnly Branded Type

In TypeScript, a primitive string can contain anything: "2026-11-02", "invalid-string", or an arbitrary uuid.

DaySafe introduces a nominal branded type, DateOnly, which represents a strictly validated Gregorian calendar date in YYYY-MM-DD format:

DateOnly definition
400 font-semibold">type DateOnly = string & {
400 font-semibold">readonly __brand: 400 font-semibold">class="text-emerald-400">"DateOnly";
};

You cannot arbitrarily assign an unvalidated string to a DateOnly variable. It can only be created via parseDate() or narrowed via isValidDate().

Type Narrowing with isValidDate()

isValidDate(input) is a TypeScript type guard (input is DateOnly). Inside any conditional block where it returns true, TypeScript automatically narrows the variable:

type-guard.ts
400 font-semibold">import { isValidDate, DateOnly } 400 font-semibold">from 400 font-semibold">class="text-emerald-400">"daysafe";
400 font-semibold">function processBooking(userInput: unknown) {
400 font-semibold">if (isValidDate(userInput)) {
// userInput is now typed as DateOnly!
400 font-semibold">const validatedDate: DateOnly = userInput;
console.log(400 font-semibold">class="text-emerald-400">"Safe date confirmed:", validatedDate);
} 400 font-semibold">else {
// TypeScript knows userInput is NOT a valid DateOnly
400 font-semibold">throw 400 font-semibold">new Error(400 font-semibold">class="text-emerald-400">"Invalid date provided");
}
}

Complete Exported Types Reference

type DateInput = DateOnly | string

All DaySafe operation arguments accept DateInput, allowing you to pass either branded DateOnly values or literal ISO strings without tedious type casting.

interface DateParts

DateParts interface
400 font-semibold">interface DateParts {
/** Gregorian year (1 - 9999) */
year: number;
/** Gregorian month (1 = January, 12 = December) */
month: number;
/** Day of the month (1 - 31) */
day: number;
}

interface FormatDateOptions

FormatDateOptions interface
400 font-semibold">interface FormatDateOptions {
locale?: string | string[];
style?: 400 font-semibold">class="text-emerald-400">"full" | 400 font-semibold">class="text-emerald-400">"long" | 400 font-semibold">class="text-emerald-400">"medium" | 400 font-semibold">class="text-emerald-400">"short";
dateStyle?: 400 font-semibold">class="text-emerald-400">"full" | 400 font-semibold">class="text-emerald-400">"long" | 400 font-semibold">class="text-emerald-400">"medium" | 400 font-semibold">class="text-emerald-400">"short";
year?: 400 font-semibold">class="text-emerald-400">"numeric" | 400 font-semibold">class="text-emerald-400">"2-digit";
month?: 400 font-semibold">class="text-emerald-400">"numeric" | 400 font-semibold">class="text-emerald-400">"2-digit" | 400 font-semibold">class="text-emerald-400">"long" | 400 font-semibold">class="text-emerald-400">"short" | 400 font-semibold">class="text-emerald-400">"narrow";
day?: 400 font-semibold">class="text-emerald-400">"numeric" | 400 font-semibold">class="text-emerald-400">"2-digit";
weekday?: 400 font-semibold">class="text-emerald-400">"long" | 400 font-semibold">class="text-emerald-400">"short" | 400 font-semibold">class="text-emerald-400">"narrow";
era?: 400 font-semibold">class="text-emerald-400">"long" | 400 font-semibold">class="text-emerald-400">"short" | 400 font-semibold">class="text-emerald-400">"narrow";
}

type ComparisonResult = -1 | 0 | 1

Strict 3-way chronological ordering result. Guaranteed to be directly compatible with Array.prototype.sort(compareDates).

Dual ESM & CommonJS Declarations

DaySafe ships with full type definitions for both modern ESM bundlers (dist/index.d.mts) and legacy CommonJS environments (dist/index.d.ts). TypeScript resolve declarations cleanly regardless of whether your project uses "moduleResolution": "NodeNext" or "bundler".