====== TypeScript ====== Useful TypeScript snippets and patterns. ===== Type Utilities ===== ==== Make specific properties optional ==== type PartialBy = Omit & Partial>; interface User { id: string; name: string; email: string; } // id is now optional type CreateUserInput = PartialBy; ==== Extract union of object values ==== const STATUS = { active: "active", inactive: "inactive", pending: "pending", } as const; type Status = (typeof STATUS)[keyof typeof STATUS]; // "active" | "inactive" | "pending" ==== Exhaustive switch checking ==== function assertNever(x: never): never { throw new Error(`Unexpected value: ${x}`); } type Shape = { kind: "circle"; radius: number } | { kind: "square"; side: number }; function area(shape: Shape): number { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "square": return shape.side ** 2; default: return assertNever(shape); // compiler error if a case is missed } } ===== Zod (Runtime Validation) ===== ==== Define a schema and infer the type ==== import { z } from "zod"; const UserSchema = z.object({ id: z.string().uuid(), name: z.string().min(1), email: z.string().email(), role: z.enum(["admin", "user"]), createdAt: z.coerce.date(), }); type User = z.infer; // Parse (throws on invalid data) const user = UserSchema.parse(untrustedData); // Safe parse (returns result object) const result = UserSchema.safeParse(untrustedData); if (result.success) { console.log(result.data); } else { console.error(result.error.flatten()); } ===== Async Patterns ===== ==== Promise.allSettled with typed results ==== async function fetchAll(urls: string[], fetcher: (url: string) => Promise) { const results = await Promise.allSettled(urls.map(fetcher)); const successes = results .filter((r): r is PromiseFulfilledResult => r.status === "fulfilled") .map((r) => r.value); const failures = results .filter((r): r is PromiseRejectedResult => r.status === "rejected") .map((r) => r.reason); return { successes, failures }; } ==== Retry with exponential backoff ==== async function withRetry( fn: () => Promise, maxRetries = 3, baseDelayMs = 1000 ): Promise { for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await fn(); } catch (error) { if (attempt === maxRetries) throw error; const delay = baseDelayMs * 2 ** attempt; await new Promise((r) => setTimeout(r, delay)); } } throw new Error("Unreachable"); } ===== Generics ===== ==== Generic API response wrapper ==== interface ApiResponse { data: T; meta: { total: number; page: number; perPage: number; }; } async function fetchPaginated( url: string, schema: z.ZodType ): Promise> { const res = await fetch(url); const json = await res.json(); return { data: z.array(schema).parse(json.results), meta: json.meta, }; } ===== See Also ===== * [[javascript:libs|JS/TS Libraries and Snippets]]