User Tools

Site Tools


javascript

TypeScript

Useful TypeScript snippets and patterns.

Type Utilities

Make specific properties optional

type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
 
interface User {
  id: string;
  name: string;
  email: string;
}
 
// id is now optional
type CreateUserInput = PartialBy<User, "id">;

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<typeof UserSchema>;
 
// 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<T>(urls: string[], fetcher: (url: string) => Promise<T>) {
  const results = await Promise.allSettled(urls.map(fetcher));
 
  const successes = results
    .filter((r): r is PromiseFulfilledResult<T> => 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<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
  baseDelayMs = 1000
): Promise<T> {
  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<T> {
  data: T;
  meta: {
    total: number;
    page: number;
    perPage: number;
  };
}
 
async function fetchPaginated<T>(
  url: string,
  schema: z.ZodType<T>
): Promise<ApiResponse<T[]>> {
  const res = await fetch(url);
  const json = await res.json();
  return {
    data: z.array(schema).parse(json.results),
    meta: json.meta,
  };
}

See Also

javascript.txt · Last modified: 2026/03/27 00:51 by roger