| name | effect-option |
| description | Option vs nullable types in Effect. Use when handling optional, nullable, or potentially undefined properties. |
| user-invocable | false |
Option vs Nullable Types in Effect
Use Option<T> as the default for optional values within Effect codebases. Reserve nullable types (T | null | undefined) for interop boundaries with external APIs, DOM operations, and JSON serialization.
When to Use Option
- Modeling domain entities with optional properties
- Functions where absence is a valid, expected outcome (partial functions)
- Chaining transformations where any step might produce absence
- Distinguishing "not found" from actual errors (return
Effect<Option<A>, E>)
- Working entirely within Effect ecosystem code
- Exhaustiveness checking on absence handling
interface User {
email: Option<string>;
}
const findUser = (id: string): Effect.Effect<Option<User>, DatabaseError> =>
When to Use Nullable Types
- Interfacing with DOM APIs, external libraries, or database drivers
- Serializing to JSON where
null vs undefined semantics matter
- Writing interop layers consumed by non-Effect code
- Performance-critical hot paths (measure first)
- Optional function parameters where
param?: T syntax is clearer
Conversion at Boundaries
Convert eagerly—fromNullable at entry, getOrNull at exit:
const userOption = Option.fromNullable(externalApi.getUser());
const result = pipe(
userOption,
Option.map((u) => u.preferences),
Option.flatMap((p) => Option.fromNullable(p.theme)),
Option.filter((theme) => theme !== "system"),
Option.getOrElse(() => "dark"),
);
const response = { theme: Option.getOrNull(themeOption) };
Conversion Reference
| From | To | Method |
|---|
T | null | undefined | Option<T> | Option.fromNullable() |
Option<T> | T | null | Option.getOrNull() |
Option<T> | T | undefined | Option.getOrUndefined() |
() => T | null | (a: A) => Option<T> | Option.liftNullable() |
Schema Transformations for APIs
Schema.OptionFromNullOr(Schema.String);
Schema.OptionFromUndefinedOr(Schema.String);
Schema.OptionFromNullishOr(Schema.String);
Composing with Option
Chaining Transformations
pipe(
Option.fromNullable(user),
Option.map((u) => u.preferences),
Option.flatMap((p) => Option.fromNullable(p.theme)),
Option.filter((theme) => theme !== "system"),
Option.getOrElse(() => "dark"),
);
Generator Syntax
const result = Option.gen(function* () {
const user = yield* Option.fromNullable(maybeUser);
const prefs = yield* Option.fromNullable(user.preferences);
return prefs.theme;
});
Working with Arrays
import { Array, Option, pipe } from "effect";
const updated = Array.filterMap(skills, (s) =>
Option.map(s.locked, (locked) => ({ ...s, version: locked.version })),
);
const lockedSkills = Array.getSomes(Array.map(skills, (s) => s.locked));
Pattern Matching
Option.match(userOption, {
onNone: () => "Anonymous",
onSome: (user) => user.displayName,
});
if (Option.isSome(opt)) {
opt.value;
}
Option.getOrElse(() => fallback);
Option.getOrThrow(opt);
Lifting into Effect
Effect.fromOption(opt).pipe(Effect.mapError(() => new NotFoundError()));
const findUser = (id: string): Effect.Effect<Option<User>, DbError> =>
Effect.gen(function* () {
const result = yield* db.query(id);
return Option.fromNullable(result);
});
Effect Optional Checklist