TypeScriptを用いた開発において、関数の柔軟性を高めるために欠かせない機能の一つがオプショナル引数です。

関数の定義時に引数名の後ろに ? を付与することで、その引数を呼び出し時に省略可能にすることができます。

しかし、単に省略できるだけでなく、型定義や実行時の挙動において注意すべき実践的なルールがいくつか存在します。

本記事では、2026年現在のモダンな開発環境を前提とした、オプショナル引数の正しい使い方と陥りやすい注意点について解説します。

TypeScriptのオプショナル引数とは

オプショナル引数は、関数を呼び出す際に特定の値を渡さなくてもエラーにならないように定義する仕組みです。

JavaScriptでは引数の数に制限がなく、定義されていない引数は自動的に undefined となりますが、TypeScriptでは厳格な型チェックが行われます。

オプショナル引数を使用することで、TypeScriptの型安全性を維持したまま、柔軟なインターフェースを設計することが可能になります。

基本的な構文

オプショナル引数を定義するには、引数名の直後に ? を記述します。

TypeScript
// nameは必須、ageは省略可能な引数
function greet(name: string, age?: number): string {
  if (age !== undefined) {
    return `こんにちは、${name}さん。あなたは${age}歳ですね。`;
  }
  return `こんにちは、${name}さん。`;
}

// 両方のパターンで呼び出し可能
console.log(greet("田中", 25));
console.log(greet("佐藤"));
実行結果
"こんにちは、田中さん。あなたは25歳ですね。"
"こんにちは、佐藤さん。"

型定義上の意味

オプショナル引数として定義された変数は、内部的には 指定した型と undefined のユニオン型 として扱われます。

例えば age?: number は、実質的に number | undefined と同等です。

ただし、明示的に number | undefined と定義した場合とは異なり、呼び出し時にその引数自体を省略できるかどうかが大きな違いとなります。

オプショナル引数の実践ルール

オプショナル引数を効果的に活用するためには、設計段階で守るべきいくつかの重要なルールがあります。

引数の順番を厳守する

TypeScriptの仕様として、オプショナル引数は必ず必須引数の後に配置しなければならない というルールがあります。

もしオプショナル引数を最初に配置してしまうと、コンパイラはどの引数が省略されたのかを判断できなくなるためです。

TypeScript
// エラーになる例
// function badFunc(age?: number, name: string) { ... }

// 正しい例
function goodFunc(name: string, age?: number) {
  console.log(name, age);
}

undefinedチェックを必ず行う

オプショナル引数は呼び出し側で省略される可能性があるため、関数内部では常に undefined である可能性を考慮する必要があります。

値が存在することを前提とした処理を記述すると、実行時にエラーが発生したり、意図しない計算結果になったりするリスクがあります。

if 文によるガード節や、Optional Chaining を活用して安全にアクセスするようにしましょう。

デフォルト引数との使い分けを明確にする

オプショナル引数と似た機能に「デフォルト引数」がありますが、これらは明確に使い分けるべきです。

機能構文主なユースケース
オプショナル引数arg?: type値がない状態(undefined)を許容し、ロジックを分岐させる場合
デフォルト引数arg = value値が省略された際に、特定の標準値を使用したい場合

値が省略された際に特定の「初期値」を代入したい場合は、デフォルト引数を使用するのがベストプラクティスです。

なぜなら、デフォルト引数を使用すると関数のシグネチャから undefined が排除され、型安全性がより強固になるためです。

オプショナル引数を使用する際の注意点

便利なオプショナル引数ですが、多用しすぎるとコードの可読性やメンテナンス性を損なう可能性があります。

引数の数が増えすぎないように注意する

一つの関数に複数のオプショナル引数を設定すると、呼び出し側で「どの引数が何番目か」を把握するのが難しくなります。

特に3つ以上の引数がある場合は、オブジェクト形式(引数オブジェクト)として定義することを検討してください。

TypeScript
// メンテナンス性が低い例
function createUser(name: string, age?: number, gender?: string, address?: string) { ... }

// 推奨される例(オプションオブジェクト)
interface UserOptions {
  age?: number;
  gender?: string;
  address?: string;
}

function createUserBetter(name: string, options: UserOptions) {
  const { age, gender, address } = options;
  // 処理...
}

コールバック関数でのオプショナル引数

コールバック関数を引数として受け取る場合、そのコールバック自体の引数をオプショナルにすることは避けるべきです。

コールバックの実装側で引数が存在するかどうかを確認させる手間が発生し、設計を複雑にしてしまうためです。

呼び出し側で不要な引数を無視することは JavaScript の言語仕様上可能であるため、型定義では必須として扱うのが一般的です。

厳密な null チェック(strictNullChecks)の影響

TypeScriptの設定で strictNullChecks が有効になっている場合、オプショナル引数の扱いはより厳格になります。

この設定下では、undefined を許容しない型に対して、チェックなしでオプショナル引数を渡すことはできません。

プロジェクトの tsconfig.json の設定を確認し、コンパイラがどの程度の厳密さを求めているかを把握しておきましょう。

高度な応用パターン

さらに実践的な開発では、オプショナル引数と他の機能を組み合わせる場面が多くあります。

型ガードによる型の絞り込み

オプショナル引数が存在するかどうかで処理を分ける際、typeof 演算子や単純な真偽値判定によって型の絞り込み(Type Narrowing)が行われます。

TypeScript
function processValue(value?: string | number) {
  if (typeof value === "string") {
    // ここでは value は string 型として確定する
    console.log(value.toUpperCase());
  } else if (typeof value === "number") {
    // ここでは value は number 型として確定する
    console.log(value.toFixed(2));
  }
}

インターフェースのオプショナルプロパティとの連携

関数の引数だけでなく、インターフェースや型エイリアスでも ? を利用したオプショナルプロパティが定義できます。

APIのレスポンスや設定オブジェクトなどを定義する際、特定のフィールドが欠落していても型エラーにならないように設計できます。

ただし、「存在しない」ことと「nullである」ことはTypeScript上でも意味が異なるため、データ構造の設計には注意が必要です。

まとめ

TypeScriptのオプショナル引数(?)は、柔軟な関数設計を実現するための強力なツールです。

引数を省略可能にすることで、呼び出し側のコードを簡潔に保つことができますが、その反面、関数内部での undefined チェックが不可欠となります。

「引数が必須ではない理由」を明確にし、デフォルト値で代用できないか検討することが、質の高いコードを書くためのポイントです。

また、引数の数が増える場合はオブジェクトにまとめるなど、常に読み手や利用者の視点に立ったインターフェース設計を心がけましょう。

今回紹介した実践ルールを守ることで、実行時エラーのリスクを最小限に抑えつつ、TypeScriptの恩恵を最大限に享受できるはずです。