Skip to content
GreenSnippets

A typed Result type for error handling without exceptions

Model success and failure as a discriminated union so callers must handle both cases.

TypeScript
type Ok<T> = { ok: true; value: T };
type Err<E> = { ok: false; error: E };
type Result<T, E = Error> = Ok<T> | Err<E>;

const ok = <T>(value: T): Ok<T> => ({ ok: true, value });
const err = <E>(error: E): Err<E> => ({ ok: false, error });

function parsePort(input: string): Result<number, string> {
  const n = Number(input);
  if (!Number.isInteger(n) || n < 1 || n > 65535) return err(`invalid port: ${input}`);
  return ok(n);
}

Example / expected output

Output
parsePort("8080") // { ok: true, value: 8080 }
parsePort("abc")  // { ok: false, error: "invalid port: abc" }

How it works

Exceptions are invisible in function signatures. A Result<T, E> makes the failure path part of the type, and TypeScript narrows it after you check ok.

Use it at boundaries where failure is expected (parsing, validation, network), and keep exceptions for genuine bugs.