grahamcrackers/skills

typescript-advanced-patterns

Advanced TypeScript type-level patterns including generics, branded types, discriminated unions, template literals, conditional types, and type-safe builders.

First seen Feb 26, 2026

Installation

$ npx skills add grahamcrackers/skills --skill typescript-advanced-patterns

Summary

  • Advanced TypeScript type-level patterns including generics, branded types, discriminated unions, template literals, conditional types, and type-safe builders.
  • Use when working on complex TypeScript type design, creating type-safe APIs, building libraries, or when the user asks about advanced type patterns.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from grahamcrackers/skills · top by installs.

npx skills add grahamcrackers/skills

Browse all from grahamcrackers/skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

License LICENSE
Default branch main
Open issues 0
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 4,817 B
  • docs SUMMARY.md 343 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 9 installs

SKILL.md

TypeScript Advanced Patterns

Discriminated Unions

Model state machines and mutually exclusive variants with a shared literal discriminant:

type Result<T, E = Error> = { success: true; data: T } | { success: false; error: E };

function handle(result: Result<User>) {
    if (result.success) {
        console.log(result.data.name); // narrowed to { data: User }
    } else {
        console.error(result.error); // narrowed to { error: Error }
    }
}

Prefer discriminated unions over optional fields when states are mutually exclusive.

Branded / Nominal Types

Prevent accidental value interchange between structurally identical types:

type Brand<T, B extends string> = T & { readonly __brand: B };

type UserId = Brand<string, "UserId">;
type OrderId = Brand<string, "OrderId">;

function getUser(id: UserId): User {
    /* ... */
}

const userId = "abc" as UserId;
const orderId = "abc" as OrderId;
getUser(orderId); // Type error

Use branded types for IDs, validated strings, currency amounts, and other domain primitives.

Template Literal Types

Build type-safe string patterns:

type EventName = `on${Capitalize<string>}`;
type CSSUnit = `${number}${"px" | "rem" | "em" | "%"}`;
type Route = `/${string}`;

Combine with mapped types for type-safe event handlers, CSS-in-JS, or API routes.

Conditional Types

Derive types based on conditions:

type IsArray<T> = T extends readonly unknown[] ? true : false;

type Unwrap<T> = T extends Promise<infer U> ? Unwrap<U> : T extends readonly (infer U)[] ? U : T;

Use infer to extract inner types. Keep conditional types shallow — deeply nested conditionals become unreadable.

Mapped Types with Key Remapping

Transform object types:

type Getters<T> = {
    [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

Type-Safe Builder Pattern

Chain methods while accumulating type information:

class QueryBuilder<T extends Record<string, unknown>> {
    select<K extends keyof T>(...keys: K[]): QueryBuilder<Pick<T, K>> {
        return this as unknown as QueryBuilder<Pick<T, K>>;
    }

    where<K extends keyof T>(key: K, value: T[K]): QueryBuilder<T> {
        return this;
    }
}

Exhaustive Pattern Matching

Ensure all union variants are handled at compile time:

function assertNever(value: never): never {
    throw new Error(`Unhandled value: ${value}`);
}

function handleStatus(status: Status) {
    switch (status) {
        case "active":
            return "Active";
        case "inactive":
            return "Inactive";
        default:
            return assertNever(status); // compile error if a variant is missed
    }
}

Function Overloads

Provide precise signatures for functions with varying input/output types:

function parse(input: string): Document;
function parse(input: Buffer): Document;
function parse(input: string | Buffer): Document {
    // implementation
}

Prefer discriminated union parameters or generics over overloads when possible — overloads add complexity.

const Type Parameters

Preserve literal types through generic functions (TypeScript 5.0+):

function createRoutes<const T extends readonly string[]>(routes: T): T {
    return routes;
}

const routes = createRoutes(["/home", "/about"]); // readonly ["/home", "/about"]

Type-Safe Record Factories

Constrain records to specific key sets:

function createLookup<K extends string, V>(keys: readonly K[], valueFn: (key: K) => V): Record<K, V> {
    return Object.fromEntries(keys.map((k) => [k, valueFn(k)])) as Record<K, V>;
}

Module Augmentation

Extend third-party types without forking:

declare module "express" {
    interface Request {
        user?: AuthenticatedUser;
    }
}

Keep augmentations in a dedicated *.d.ts file and reference it in tsconfig.json.

Guidelines

  • Favor readability over cleverness — if a type requires a paragraph to explain, simplify it.
  • Test complex types with Expect / Equal utilities (e.g., from type-testing or expect-type).
  • Document non-obvious type-level logic with a brief comment above the type.
  • Avoid recursive types deeper than 3-4 levels — they can crash the compiler or produce cryptic errors.