fdu logo@pyyupsk/fdu - Faster Date-Time Utility
Introduction

TypeScript Guide

Comprehensive TypeScript guide for @pyyupsk/fdu. Learn about full type inference, strict null safety, custom type guards, locale typing, plugin development with TypeScript, and advanced typing patterns for type-safe date manipulation.

fdu is built with TypeScript from the ground up, providing excellent type safety and IntelliSense support.

Installation

No additional @types/* package needed - TypeScript types are included:

npm install @pyyupsk/fdu

Basic Types

FduInstance

The main date instance type:

import { fdu, type FduInstance } from "@pyyupsk/fdu";

const date: FduInstance = fdu("2025-09-30");

// Full IntelliSense support
date.format("YYYY-MM-DD"); // ✅
date.add(1, "day"); // ✅
date.unknown(); // ❌ TypeScript error

Input Types

fdu accepts multiple input types, all fully typed:

import { fdu, type FduInput } from "@pyyupsk/fdu";

// All valid inputs
const date1: FduInstance = fdu(); // undefined
const date2: FduInstance = fdu("2025-09-30"); // string
const date3: FduInstance = fdu(1727704200000); // number
const date4: FduInstance = fdu(new Date()); // Date
const date5: FduInstance = fdu(date1); // FduInstance
const date6: FduInstance = fdu({ year: 2025 }); // ObjectInput

// Type-safe helper
function processDate(input: FduInput): FduInstance {
  return fdu(input);
}

ObjectInput

Type-safe object input for date creation:

import { type ObjectInput } from "@pyyupsk/fdu";

const dateObj: ObjectInput = {
  year: 2025,
  month: 9, // Optional
  day: 30, // Optional
  hour: 14, // Optional
  minute: 30, // Optional
  second: 0, // Optional
  millisecond: 0, // Optional
};

const date = fdu(dateObj);

// TypeScript catches errors
const invalid: ObjectInput = {
  year: "2025", // ❌ Error: string not assignable to number
  month: 13, // ⚠️ Type allows it, but runtime will create invalid date
};

Unit Types

All manipulation and comparison methods use typed units:

import { type UnitType } from "@pyyupsk/fdu";

const date = fdu("2025-09-30");

// Type-safe units
date.add(1, "day"); // ✅
date.add(2, "month"); // ✅
date.add(1, "invalid"); // ❌ TypeScript error

// Unit abbreviations also typed
date.add(1, "y"); // ✅ year
date.add(1, "M"); // ✅ month
date.add(1, "d"); // ✅ day
date.add(1, "h"); // ✅ hour
date.add(1, "m"); // ✅ minute
date.add(1, "s"); // ✅ second
date.add(1, "ms"); // ✅ millisecond

// Type definition
type UnitType =
  | "year"
  | "y"
  | "month"
  | "M"
  | "week"
  | "w"
  | "day"
  | "d"
  | "hour"
  | "h"
  | "minute"
  | "m"
  | "second"
  | "s"
  | "millisecond"
  | "ms";

Locale Types

Type-safe locale configuration:

import { type LocaleConfig } from "@pyyupsk/fdu";

const customLocale: LocaleConfig = {
  name: "custom",
  months: [
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December",
  ],
  monthsShort: [
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec",
  ],
  weekdays: [
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
  ],
  weekdaysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
  weekdaysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
  weekStart: 0, // 0 = Sunday, 1 = Monday
  formats: {
    LT: "h:mm A",
    LTS: "h:mm:ss A",
    L: "MM/DD/YYYY",
    LL: "MMMM D, YYYY",
    LLL: "MMMM D, YYYY h:mm A",
    LLLL: "dddd, MMMM D, YYYY h:mm A",
  },
  ordinal: (n: number) => {
    const s = ["th", "st", "nd", "rd"];
    const v = n % 100;
    return n + (s[(v - 20) % 10] || s[v] || s[0] || "th");
  },
  meridiem: (hour: number, minute: number, isLowercase: boolean) => {
    return hour < 12 ? (isLowercase ? "am" : "AM") : isLowercase ? "pm" : "PM";
  },
  // Optional: For non-Gregorian calendars
  yearOffset: 543, // e.g., Buddhist Era
};

Plugin Types

Type-safe plugin development:

import { type Plugin, type PluginAPI } from "@pyyupsk/fdu";

// Define plugin with options
interface MyPluginOptions {
  prefix?: string;
}

const myPlugin: Plugin<MyPluginOptions> = {
  name: "my-plugin",
  version: "1.0.0", // Optional
  install(api: PluginAPI, options?: MyPluginOptions) {
    const prefix = options?.prefix ?? "Date: ";

    // Extend prototype with full type safety
    api.extendPrototype("customFormat", function () {
      return prefix + this.format("YYYY-MM-DD");
    });
  },
};

// Use plugin
fdu.extend(myPlugin, { prefix: "Today: " });

// Extend FduInstance interface for IntelliSense
declare module "@pyyupsk/fdu" {
  interface FduInstance {
    customFormat(): string;
  }
}

// Now TypeScript knows about the method
const date = fdu("2025-09-30");
date.customFormat(); // ✅ TypeScript recognizes this

Strict Null Safety

fdu is designed for strictNullChecks:

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "strictNullChecks": true
  }
}
import { fdu } from "@pyyupsk/fdu";

// All methods return non-nullable types
const date = fdu("2025-09-30");
const formatted: string = date.format("YYYY-MM-DD"); // ✅ never null
const year: number = date.year(); // ✅ never null

// Invalid dates are still FduInstance
const invalid = fdu("invalid");
invalid.isValid(); // false, but instance is valid

Type Guards

Create custom type guards:

import { type FduInstance } from "@pyyupsk/fdu";

function isFduInstance(value: unknown): value is FduInstance {
  return (
    typeof value === "object" &&
    value !== null &&
    "format" in value &&
    "isValid" in value
  );
}

function processDate(input: unknown) {
  if (isFduInstance(input)) {
    // TypeScript knows input is FduInstance
    return input.format("YYYY-MM-DD");
  }
  return "Not a date";
}

Generic Utilities

Type-safe date utilities:

import { fdu, type FduInstance } from "@pyyupsk/fdu";

// Generic date processor
function processDates<T extends Record<string, unknown>>(
  obj: T,
  dateKeys: (keyof T)[],
): T {
  const result = { ...obj };

  for (const key of dateKeys) {
    const value = result[key];
    if (typeof value === "string" || typeof value === "number") {
      result[key] = fdu(value) as T[typeof key];
    }
  }

  return result;
}

interface User {
  name: string;
  createdAt: string;
  updatedAt: string;
}

const user: User = {
  name: "John",
  createdAt: "2025-01-01",
  updatedAt: "2025-09-30",
};

const processed = processDates(user, ["createdAt", "updatedAt"]);

Advanced Patterns

Branded Types

Create domain-specific date types:

type BirthDate = FduInstance & { readonly __brand: "BirthDate" };
type EventDate = FduInstance & { readonly __brand: "EventDate" };

function createBirthDate(input: FduInput): BirthDate {
  return fdu(input) as BirthDate;
}

function createEventDate(input: FduInput): EventDate {
  return fdu(input) as EventDate;
}

const birth = createBirthDate("1990-01-01");
const event = createEventDate("2025-09-30");

// Type-safe functions
function calculateAge(birthDate: BirthDate): number {
  return fdu().diff(birthDate, "year");
}

calculateAge(birth); // ✅
calculateAge(event); // ❌ TypeScript error

Readonly Dates

Make dates immutable at type level:

type ReadonlyFduInstance = Readonly<FduInstance>;

function freezeDate(date: FduInstance): ReadonlyFduInstance {
  return Object.freeze(date) as ReadonlyFduInstance;
}

const date = fdu("2025-09-30");
const frozen = freezeDate(date);

frozen.format("YYYY-MM-DD"); // ✅ Getters work
frozen.add(1, "day"); // ✅ Returns new instance (still immutable)

Configuration

tsconfig.json

Recommended TypeScript configuration:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "lib": ["ES2020", "DOM"],
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true
  }
}

Common Issues

Import Types

Always import types separately for better tree-shaking:

// ✅ Good - tree-shakeable
import { fdu } from "@pyyupsk/fdu";
import type { FduInstance, FduInput } from "@pyyupsk/fdu/types";

// ❌ Avoid - may impact bundle size
import { fdu, FduInstance, FduInput } from "@pyyupsk/fdu";

Module Augmentation

When adding plugin methods, use module augmentation:

// types.d.ts or in your plugin file
declare module "@pyyupsk/fdu" {
  interface FduInstance {
    myCustomMethod(): string;
  }
}

On this page