====== 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]]