Real World Example

Bank Transfer With Typed Errors

A banking or UPI app must tell the user exactly why a transfer failed: wrong PIN, low balance or daily limit. Expected failures are Results; a broken database connection is an exception that bubbles up to a global handler which logs it and returns HTTP 500.

Example

type TransferError =
  | { code: "INSUFFICIENT_FUNDS"; balance: number }
  | { code: "DAILY_LIMIT"; remaining: number }
  | { code: "SAME_ACCOUNT" };
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };

class DatabaseError extends Error { name = "DatabaseError"; }

const balances: Record<string, number> = { A1: 5000, B2: 100 };
const DAILY_LIMIT = 10000;
let sentToday = 8000;
let dbUp = true;

function transfer(from: string, to: string, amount: number): Result<string, TransferError> {
  if (!dbUp) throw new DatabaseError("connection refused");
  if (from === to) return { ok: false, error: { code: "SAME_ACCOUNT" } };
  const bal = balances[from] ?? 0;
  if (amount > bal) return { ok: false, error: { code: "INSUFFICIENT_FUNDS", balance: bal } };
  if (sentToday + amount > DAILY_LIMIT) return { ok: false, error: { code: "DAILY_LIMIT", remaining: DAILY_LIMIT - sentToday } };
  balances[from] = bal - amount;
  balances[to] = (balances[to] ?? 0) + amount;
  sentToday += amount;
  return { ok: true, value: "TXN" + (1000 + sentToday) };
}

function message(e: TransferError): string {
  switch (e.code) {
    case "INSUFFICIENT_FUNDS": return `Low balance (Rs. ${e.balance})`;
    case "DAILY_LIMIT": return `Daily limit reached, Rs. ${e.remaining} left today`;
    case "SAME_ACCOUNT": return "Cannot send to the same account";
  }
}

function handle(from: string, to: string, amount: number): void {
  try {
    const r = transfer(from, to, amount);
    console.log(r.ok ? "Success " + r.value : "Failed: " + message(r.error));
  } catch (e) {
    console.log("HTTP 500, logged:", e instanceof Error ? e.name + ": " + e.message : e);
  }
}

handle("A1", "B2", 1500);
handle("B2", "A1", 5000);
handle("A1", "B2", 1000);
handle("A1", "A1", 10);
dbUp = false;
handle("A1", "B2", 10);

Output

Success TXN10500
Failed: Low balance (Rs. 1600)
Failed: Daily limit reached, Rs. 500 left today
Failed: Cannot send to the same account
HTTP 500, logged: DatabaseError: connection refused