typescript
43 lines · 8 steps
A Result type for typed error handling
A discriminated union that models success or failure as a value, with combinators to transform it without throwing.
Explained by
highlit
1type Ok<T> = { ok: true; value: T };
2type Err<E> = { ok: false; error: E };
3export type Result<T, E> = Ok<T> | Err<E>;
4
5export const ok = <T>(value: T): Ok<T> => ({ ok: true, value });
6export const err = <E>(error: E): Err<E> => ({ ok: false, error });
7
8export function isOk<T, E>(result: Result<T, E>): result is Ok<T> {
9 return result.ok;
10}
11
12export function map<T, U, E>(
13 result: Result<T, E>,
14 fn: (value: T) => U,
15): Result<U, E> {
16 return result.ok ? ok(fn(result.value)) : result;
17}
18
19export function andThen<T, U, E>(
20 result: Result<T, E>,
21 fn: (value: T) => Result<U, E>,
22): Result<U, E> {
23 return result.ok ? fn(result.value) : result;
24}
25
26export function mapErr<T, E, F>(
27 result: Result<T, E>,
28 fn: (error: E) => F,
29): Result<T, F> {
30 return result.ok ? result : err(fn(result.error));
31}
32
33export function unwrapOr<T, E>(result: Result<T, E>, fallback: T): T {
34 return result.ok ? result.value : fallback;
35}
36
37export function fromThrowable<T>(fn: () => T): Result<T, Error> {
38 try {
39 return ok(fn());
40 } catch (e) {
41 return err(e instanceof Error ? e : new Error(String(e)));
42 }
43}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Modeling failure as a value instead of an exception makes error paths explicit and type-checked.
- 2A shared boolean tag lets TypeScript narrow a union to exactly one variant after a truthiness check.
- 3Small combinators like map and andThen let you chain fallible steps while short-circuiting on the first error.
Related explainers
javascript
async function copyToClipboard(text) { if (navigator.clipboard && window.isSecureContext) { try { await navigator.clipboard.writeText(text);
Copying to the clipboard with a fallback
clipboard
progressive-enhancement
dom
Intermediate
8 steps
rust
use std::time::Duration; use tokio::time::sleep; #[derive(Debug)]
Async retry with exponential backoff in Rust
retry
exponential-backoff
async
Intermediate
8 steps
typescript
interface PollOptions<T> { intervalMs?: number; timeoutMs?: number; signal?: AbortSignal;
A cancellable polling helper in TypeScript
polling
async-await
abortsignal
Intermediate
9 steps
rust
use std::fs; use std::io; use std::path::{Path, PathBuf};
Recursive file search by extension in Rust
recursion
filesystem
error-handling
Intermediate
8 steps
javascript
'use server' import { z } from 'zod' import { redirect } from 'next/navigation'
How a Next.js Server Action validates a form
server-actions
form-validation
zod
Intermediate
7 steps
java
public final class RetryExecutor { private final int maxAttempts; private final Duration initialDelay;
Exponential backoff retry in Java
retry
exponential-backoff
jitter
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/a-result-type-for-typed-error-handling-explained-typescript-0b6c/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.