typescript-advanced-patterns

📁 grahamcrackers/skills 📅 2 days ago
3
总安装量
2
周安装量
#61685
全站排名
安装命令
npx skills add https://github.com/grahamcrackers/skills --skill typescript-advanced-patterns

Agent 安装分布

cline 2
gemini-cli 2
github-copilot 2
codex 2
kimi-cli 2
cursor 2

Skill 文档

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.