Option / Result 型

null や例外の代わりに、値の有無やエラーを型で表現する関数型プログラミングのパターン

型システム品質

Option / Result 型とは

Option 型 (Maybe 型) と Result 型は、null や例外の代わりに、値の有無やエラーを型で表現するパターンである。Rust、Haskell、Scala で標準的に使われ、TypeScript でも Discriminated Union で実装できる。

null の問題

// ❌ null を返す: 呼び出し側が null チェックを忘れるとランタイムエラー
function findUser(id: string): User | null {
  return db.get(id); // null かもしれない
}
const user = findUser('123');
console.log(user.name); // 💥 TypeError: Cannot read property 'name' of null

TypeScript での Option 型

type Option<T> = { kind: 'some'; value: T } | { kind: 'none' };

const some = <T>(value: T): Option<T> => ({ kind: 'some', value });
const none: Option<never> = { kind: 'none' };

function findUser(id: string): Option<User> {
  const user = db.get(id);
  return user ? some(user) : none;
}

const result = findUser('123');
if (result.kind === 'some') {
  console.log(result.value.name); // ✅ 型安全
}

TypeScript での Result 型

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

function parseJson<T>(raw: string): Result<T, string> {
  try {
    return { ok: true, value: JSON.parse(raw) };
  } catch (e) {
    return { ok: false, error: `Parse error: ${e}` };
  }
}

const result = parseJson<User>('{"name":"Alice"}');
if (result.ok) {
  console.log(result.value.name); // ✅ 型安全
} else {
  console.error(result.error);    // ✅ エラーも型安全
}

Rust の Option / Result

// Option<T>: Some(T) or None
fn find_user(id: &str) -> Option<User> {
    db.get(id) // None を返す可能性がある
}

// Result<T, E>: Ok(T) or Err(E)
fn parse_config(path: &str) -> Result<Config, io::Error> {
    let content = fs::read_to_string(path)?; // ? で早期リターン
    Ok(toml::from_str(&content)?)
}

try-catch との比較

観点 try-catch Result 型
エラーの型 any (TypeScript) 明示的な型
網羅性チェック なし コンパイラが検証
エラーの伝播 throw ? 演算子 (Rust)
パフォーマンス スタックの巻き戻し 通常の戻り値

neverthrow (TypeScript ライブラリ)

import { ok, err, Result } from 'neverthrow';

function divide(a: number, b: number): Result<number, string> {
  if (b === 0) return err('Division by zero');
  return ok(a / b);
}

divide(10, 0)
  .map(v => v * 2)
  .mapErr(e => `Error: ${e}`);

より深く学ぶには関連書籍が役立つ。

関連用語