Skip to content

Use TypeScript as Intended

TypeScript was created to help developers write safer and more maintainable JavaScript. It enforces static typing, catches errors earlier, improves IDE support, and documents intent directly in code. To fully benefit from it, avoid shortcuts and use TypeScript's advanced features as they were designed.

Key practices

  • any is prohibited. Use unknown at boundaries, then narrow it down.
  • Prefer type for unions/mapped types; interface for extensible object shapes.
  • Use generics with constraints and defaults. Extract generic helpers instead of duplication.
  • Use literal unions or enum for closed sets. Centralize labels and helpers.
  • Enforce exhaustiveness with never in discriminated unions.

Discriminated unions

ts
type ApiResponse =
  | { status: "success"; data: string }
  | { status: "error"; message: string };

function handleResponse(res: ApiResponse) {
  switch (res.status) {
    case "success":
      return res.data;
    case "error":
      return res.message;
    default:
      const _exhaustive: never = res;
      return _exhaustive;
  }
}

typeof keyword

ts
const ROLES = ["admin", "editor", "viewer"] as const;
type Role = typeof ROLES[number]; // "admin" | "editor" | "viewer"

function setRole(role: Role) {
  // role is strictly validated
}

Generics and wildcards

ts
interface BaseResponse<T> {
  status: number;
  data: T;
}

function fetchResource<T>(url: string): Promise<BaseResponse<T>> {
  return fetch(url).then((res) => res.json());
}

// Usage
type User = { id: number; name: string };
const userResponse = fetchResource<User>("/api/user");