引数名のあとに ? を付けると、その引数は省略可能になります。呼び出し側は省略でき、関数の中での型には undefined が含まれます。
TypeScriptは引数の数をチェックするので、? がなければ最初の呼び出しはコンパイルエラーになります: Expected 2 arguments, but got 1.(TS2554)。
省略可能な引数は undefined かもしれない
呼び出し側が省略できるので、省略可能な引数の関数内での型は T | undefined です。strictNullChecks のもとでは、T として使う前に undefined の場合を処理する必要があります。
ここでよく使うのはオプショナルチェーン(?.)と Null 合体演算子(??)です。代わりの値が決まっているなら、デフォルト引数のほうが短く書けます。
デフォルト引数
デフォルト値を付けると、呼び出し側にとって引数は省略可能になり、関数の中では undefined を含まない型になります。型はデフォルト値から推論されるので、型注釈はたいてい不要です。
呼び出し側から見たシグネチャは repeat(text: string, times?: number, separator?: string) です。デフォルト値はJavaScriptのルールに従います。引数が undefined のとき(省略した場合も明示的に渡した場合も)に使われ、null のときは使われません。デフォルト値の式では前の引数を使えます: function range(start: number, end = start + 10)。
引数の順序のルール
| 宣言 | コンパイルできるか | 備考 |
|---|---|---|
(a: number, b?: number) | はい | 省略可能な引数は最後に置く |
(a?: number, b: number) | いいえ | TS1016: A required parameter cannot follow an optional parameter. |
(a = 0, b: number) | はい | ただしデフォルト値を使うには f(undefined, 5) と書く必要がある |
(a: number, ...rest: number[]) | はい | 残余引数は常に最後 |
(a?: number, ...rest: number[]) | はい | 残余引数の前に省略可能な引数を置くのは可 |
必須の引数の前にデフォルト値付きの引数を置くのは合法ですが、扱いにくくなります。呼び出し側から見た型は number | undefined になり、プレースホルダーとして undefined を書くのは誰にとっても面倒です。先頭の引数を柔軟にしたいなら、オプションオブジェクトか 関数のオーバーロード を使いましょう。
省略と undefined
x?: number と x: number | undefined は似ていて、関数の中では同じ型です。違うのは呼び出し側です。前者は省略でき、後者は渡さなければなりません。
値がなくてもよいが必ず渡す引数には | undefined を使い、すべての呼び出し側に考えさせます。省略するのがふつうの呼び出し方なら ? を使います。(メッセージが「1 arguments」となっているのは誤植ではなく、TypeScriptの表記です。)
残余引数
残余引数 ...name: T[] は、任意の数の引数を配列にまとめます。最後の引数でなければなりません。
固定の引数に配列をスプレッドする場合はもっと厳しくなります。残余引数には任意の number[] をスプレッドできますが、(a: number, b: number) と宣言した関数には、長さがわかるタプルしかスプレッドできません:
function point(x: number, y: number) { return { x, y }; }
const list = [3, 4]; // number[]
point(...list);
// error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
const pair = [3, 4] as const; // readonly [3, 4]
point(...pair); // fine
残余引数にはタプル型も付けられ、位置ごとに型を指定できます: ...args: [name: string, age?: number]。
オプションオブジェクト
省略可能な引数が2つか3つを超えると、呼び出し側は引数の位置がわからなくなります。デフォルト値付きのオプションオブジェクトを使えば、名前付きで順序を気にしない引数になります。
最後の = {} でオブジェクト全体が省略可能になります。これがないと fetchData("/a") はコンパイルエラー(Expected 2 arguments, but got 1.、TS2554)で、素のJavaScriptでは同じ呼び出しが実行時に TypeError を投げます。分割代入には読み取るオブジェクトが必要だからです。
コールバックの型の中の省略可能な引数
関数型の中の ? は「このコールバックの呼び出し側が渡さないかもしれない」という意味です。「コールバックが無視してよい」という意味ではありません。コールバックは後ろの引数をいつでも無視できます。ハンドラーが少ない引数を取れるようにするためだけに、コールバックの引数を省略可能にしてはいけません。
// Too loose: every handler must now cope with index being undefined
type Visit = (item: string, index?: number) => void;
// Right: the caller always passes both; handlers may use only item
type VisitStrict = (item: string, index: number) => void;
const log: VisitStrict = (item) => console.log(item);
よくある質問
TypeScriptで引数を省略可能にするには?
引数名のあとに ? を付けます: function greet(name?: string)。呼び出し側は省略でき、関数の中での型は string | undefined になるので、使う前にチェックします。デフォルト値(name = "there")を付けても省略可能になり、関数の中の undefined もなくなります。
TypeScriptで省略可能な引数を必須の引数より前に置けますか?
? では置けません。(a?: number, b: number) はエラー TS1016「A required parameter cannot follow an optional parameter.」になります。デフォルト値付きの引数は前に置けますが、デフォルト値を使うには呼び出し側が明示的に undefined を渡す必要があるので、実際には省略可能な引数とデフォルト値付きの引数は最後に置きます。
x?: number と x: number | undefined の違いは何ですか?
関数の中ではどちらも number | undefined です。違いは呼び出し側にあります。x?: number では引数を省略できますが、x: number | undefined では値が undefined であっても渡す必要があります。省略するとエラー TS2554 になります。
null を渡すとデフォルト値が使われますか?
いいえ。JavaScriptがデフォルト値を使うのは、引数が undefined のとき(省略した場合と明示的に渡した場合)だけです。null は値なので、そのまま使われます。そもそも strictNullChecks のもとでは、TypeScriptは number 型の引数への null を拒否します。
TypeScriptで配列を個別の引数として渡すには?
スプレッドします: fn(...args)。引数が固定の関数に渡す場合、配列は [number, number] のようなタプル型か、as const で作ったものでなければなりません。number[] をスプレッドすると長さがわからないので、エラー TS2556 になります。残余引数(...values: number[])へのスプレッドは常に動きます。