Option Type Pattern
In this page:
type Option<T> = { some: true; value: T } | { some: false };
Defining an Option Type
An Option type commonly represents two states: Some, wrapping a value that's present, and None, representing an absence — this turns "maybe there's a value" into something the type system can actually enforce checking on.
Example: Defining an Option Type
type Option<T> = { kind: "some"; value: T } | { kind: "none" };
const present: Option<number> = { kind: "some", value: 5 };
console.log(present);
Creating Some and None
Helper functions like some(value) and none() keep Option values concise to create while preserving their generic type parameter, so TypeScript still knows exactly what type of value might be inside.
Example: Creating Some and None
type Option<T> = { kind: "some"; value: T } | { kind: "none" };
function some<T>(value: T): Option<T> { return { kind: "some", value }; }
function none<T>(): Option<T> { return { kind: "none" }; }
console.log(some(5), none<number>());
Checking an Option
Check the discriminant field before accessing the value so TypeScript can safely narrow the union — reading straight into an Option without checking is exactly the mistake this pattern exists to prevent.
Example: Checking an Option
type Option<T> = { kind: "some"; value: T } | { kind: "none" };
function describe(opt: Option<number>): string {
return opt.kind === "some" ? `Value: ${opt.value}` : "No value";
}
console.log(describe({ kind: "some", value: 5 }));
Option Helper Operations
Helper operations can extract an Option's value or fall back to a default when it's absent, replacing a scattered chain of null checks with one composable operation.
Example: Option Helper Operations
type Option<T> = { kind: "some"; value: T } | { kind: "none" };
function getOrElse<T>(opt: Option<T>, fallback: T): T {
return opt.kind === "some" ? opt.value : fallback;
}
console.log(getOrElse<number>({ kind: "none" }, 0));
When to Use Option
Option is most useful when absence is a completely normal, expected outcome — searching for an item that might not exist, or reading a configuration value that's genuinely optional — rather than treating that absence as an exceptional error.
Example: When to Use Option
type Option<T> = { kind: "some"; value: T } | { kind: "none" };
function findConfig(key: string): Option<string> {
return key === "theme" ? { kind: "some", value: "dark" } : { kind: "none" };
}
console.log(findConfig("theme"));
- Reading
opt.valuewithout checkingkind === "some", which is a compile error. - Using
nullin one place andOptionin another, making the code inconsistent. - Forgetting to give
none<T>()its type argument so it becomesOption<unknown>.
try/catchworks in TypeScript, and caught errors should be handled with their types in mind.- Custom error classes let you create meaningful, specific error types.
- The Result and Option type patterns represent success, failure, and missing values explicitly in the type system.
Chapter Quiz — Complete all 5 topics to unlock
0/5 topics done
Complete these topics first: