オプション引数は「渡してもいいし、渡さなくてもいい」引数
まずイメージからいきます。
オプション引数は、「この引数は“あってもなくてもいい”よ」ということを型で表すための仕組みです。
書き方はとてもシンプルで、引数名のあとに ? を付けます。
function greet(name: string, title?: string) {
if (title) {
console.log(`${title} ${name}`);
} else {
console.log(name);
}
}
greet("Taro"); // OK(第二引数なし)
greet("Taro", "Dr."); // OK(第二引数あり)
TypeScripttitle?: string この ? が、「この引数は省略してもよい」という意味です。
TypeScript 的には、「この引数の型は string | undefined に近いもの」として扱われます(厳密にはちょっと違うのですが、初心者のうちはこの理解でOKです)。
ここで一番大事なのは、
「関数を呼ぶ人に対して、“この引数は必須じゃないよ”と明示できる」ということです。
オプション引数の基本ルール
省略できるかどうかを TypeScript がチェックしてくれる
普通の引数は、数がピッタリ合っていないとエラーになります。
function add(a: number, b: number) {
return a + b;
}
add(1, 2); // OK
// add(1); // エラー: Expected 2 arguments, but got 1.
TypeScriptオプション引数にすると、「渡さなくてもいい」ことになります。
function add(a: number, b?: number) {
if (b !== undefined) {
return a + b;
}
return a;
}
add(1); // OK
add(1, 2); // OK
TypeScriptここで b?: number と書くことで、
「b を渡してもいいし、渡さなくてもいい」
「渡さなかった場合、関数の中では b が undefined になりうる」
ということを、型として表現しています。
オプション引数は「後ろから」書く
TypeScript では、オプション引数は基本的に「後ろ側」に置く必要があります。
function f(a: number, b?: number, c?: string) {
// OK
}
// function g(a?: number, b: number) {} // これはエラー
TypeScript「途中だけオプション」という形は、呼び出し側の引数の並びがあいまいになるので、TypeScript は禁止しています。
つまり、
必須 → 必須 → オプション → オプション
の順番はOKだけど、
オプション → 必須
みたいな並びはNGです。
オプション引数の中身は「undefined かもしれない」として扱う
if でのチェックがほぼ必須になる
オプション引数は、「省略される可能性がある」ということなので、関数の中では「ないかもしれない」という前提で扱う必要があります。
function greet(name: string, title?: string) {
console.log(title.toUpperCase() + name); // エラー
}
TypeScriptここで TypeScript は、
「title は string だけじゃなくて undefined かもしれない。undefined に対して toUpperCase は呼べない」と教えてくれます。
正しくはこうです。
function greet(name: string, title?: string) {
if (title !== undefined) {
console.log(title.toUpperCase() + " " + name);
} else {
console.log(name);
}
}
TypeScriptif (title) とだけ書くことも多いですが、
型の意味としては「undefined の可能性をつぶしている」ということを意識しておいてください。
オプション引数とデフォルト値の違い
デフォルト引数は「省略されたときに何を使うか」を決める
オプション引数とよくセットで語られるのが「デフォルト引数」です。
function greet(name: string, title: string = "Mr.") {
console.log(`${title} ${name}`);
}
greet("Taro"); // "Mr. Taro"
greet("Taro", "Dr."); // "Dr. Taro"
TypeScriptここでは、title は省略可能ですが、
関数の中では常に string として扱われます(undefined にならない)。
つまり、
オプション引数(title?: string)
→ 「渡されないかもしれない。中身は string | undefined」
デフォルト引数(title: string = "Mr.")
→ 「呼び出し側は省略できるが、関数の中では必ず string」
という違いがあります。
実務では、
「省略されたときに特に意味を持たせない(ただの undefined)」ならオプション引数。
「省略されたときに、“これを使う”という明確な初期値がある」ならデフォルト引数。
と使い分けることが多いです。
オプション引数とオーバーロードのイメージ
ここは少し応用寄りですが、感覚をつかんでおくと後で楽になります。
オプション引数を使うと、「引数のパターン」を1つの関数でまとめられます。
function format(value: string, upper?: boolean) {
if (upper) {
return value.toUpperCase();
}
return value;
}
format("hello"); // "hello"
format("hello", true); // "HELLO"
TypeScriptこれはイメージとして、
format(s: string): string
format(s: string, upper: boolean): string
という2つの関数を、オプション引数で1つにまとめているようなものです。
TypeScript のオーバーロード(function f(...) を複数書く機能)を学ぶ前に、
「オプション引数は、“引数あり”“引数なし”の2パターンをまとめた書き方」と捉えておくと、理解がスムーズになります。
オプション引数を設計するときの感覚
「本当にオプションか?」を自分に問い直す
オプション引数は便利なので、何でも ? を付けたくなりますが、
実務的にはここが一番の落とし穴です。
この関数は、その引数がなくても本当に動くべきか?
その引数がないときの意味は、設計として決まっているか?
呼び出す側から見て、「この引数がなくてもいい」と知れることは大事か?
例えば、
function createUser(name?: string) {
// ...
}
TypeScriptと書いてしまうと、「名前なしのユーザーを作ってもいい」という意味になってしまいます。
それが本当にビジネス的にOKなのか?を考える必要があります。
もし「名前は絶対に必要」なら、オプションにせずに必須にすべきです。
function createUser(name: string) {
// ...
}
TypeScript「オプションにする」というのは、「無くてもいい」と宣言するのと同じです。
その重みを意識できるようになると、設計の質がぐっと上がります。
オプション引数とオブジェクト引数の組み合わせ
設定オブジェクトにまとめる
引数が増えてきたとき、オプション引数を乱用するよりも「オブジェクト一つにまとめる」方が読みやすくなることが多いです。
type GreetOptions = {
title?: string;
upperCase?: boolean;
};
function greet(name: string, options?: GreetOptions) {
const title = options?.title ?? "Mr.";
const formattedName = options?.upperCase ? name.toUpperCase() : name;
console.log(`${title} ${formattedName}`);
}
greet("Taro");
greet("Taro", { title: "Dr." });
greet("Taro", { upperCase: true });
greet("Taro", { title: "Prof.", upperCase: true });
TypeScriptここでは、第二引数そのものがオプションで、
その中のプロパティもそれぞれオプションです。
こうすると、
どのオプションを指定しているのかが呼び出し側から見て分かりやすい
引数が増えても、順番を気にせずに指定できる
といったメリットがあります。
まとめ:オプション引数を使うときの「軸」
最後に、オプション引数をどう捉えるといいかを整理します。
オプション引数は、「この引数はなくても呼べる」という約束。
その中身は undefined になりうるので、関数の中では「ないかもしれない」として扱う必要がある。
「省略されたときに特別な意味を持たせたい」ならデフォルト引数を使う。
「本当にその引数は必須じゃないのか?」を自分に問い直すのが設計の大事なポイント。
そして一番大事なのは、
「この関数は、どんな最低限の情報があれば動くべきか?」
この問いに答えながら、必須 と オプション を分けていくことです。
オプション引数はただの記号(?)じゃなくて、
「この関数は、ここまでは譲れるけど、ここから先は譲れない」という、あなたの設計判断そのものです。

