strictFunctionTypes
strictFunctionTypesは引数型の変性のチェックを厳しくするコンパイラオプションです。
- デフォルト: strictが有効の場合は
true、それ以外はfalse - 追加されたバージョン: 2.6
- TypeScript公式が有効化推奨
引数の双変性は安心できない
TypeScriptの関数には引数の双変性(parameter bivariance)という性質があります。どういうことか、順を追って見ていきましょう。
まず、次の3つの型の範囲を考えてみましょう。
numbernumber | nullnumber | null | undefined
numberはnumber | nullより狭い型です。number | nullの範囲には1や0.5などのnumber型とnull型があります。number型の範囲にあるのはnumber型だけです。最後のnumber | null | undefinedはこの中でもっとも範囲が広い型です。
| 型 | 範囲の広さ | 取れる値の例 |
|---|---|---|
number | 狭い | 1、0.5... |
number | null | 広い | 1、0.5...、null |
number | null | undefined | より広い | 1、0.5...、null、undefined |
続いて、次の変数funcについて考えてみましょう。この変数の型は、引数にnumber | nullを取る関数です。
tsletfunc : (n : number | null) => any;
tsletfunc : (n : number | null) => any;
この変数funcに代入できる値はどんな型でしょうか。当然、型注釈と同じ関数は問題なく代入できます。
tsfunc = (n : number | null) => {}; // OK
tsfunc = (n : number | null) => {}; // OK
引数number | nullより広いnumber | null | undefinedを受ける関数は代入できるでしょうか。これも大丈夫です。
tsfunc = (n : number | null | undefined) => {}; // OK
tsfunc = (n : number | null | undefined) => {}; // OK
このような引数型の範囲を広められる特性を引数の反変性(parameter contravariance)と言います。
引数number | nullより狭いnumberを取る関数は代入できるでしょうか。これもTypeScriptでは代入できます。
tsfunc = (n : number) => {}; // OK
tsfunc = (n : number) => {}; // OK
このような引数型の範囲を狭められる特性を引数の共変性(parameter covariance)と言います。
TypeScriptの関数型は、引数の反変性と引数の共変性の両特性を持っています。この両特性は一言で、引数の双変性と言います。
引数の双変性は危険な側面があります。nullが渡せるfunc関数に、numberだけが来ることを前提とした関数を代入しているためです。もしも、funcにnullを渡すと、実行時エラーが発生します。
ts// nullも来る可能性がある関数型letfunc : (n : number | null) => any;// numberを前提とした関数を代入func = (n : number) =>n .toString ();// funcにはnullが渡せる → 矛盾が実行時エラーを生むfunc (null);
ts// nullも来る可能性がある関数型letfunc : (n : number | null) => any;// numberを前提とした関数を代入func = (n : number) =>n .toString ();// funcにはnullが渡せる → 矛盾が実行時エラーを生むfunc (null);
こうした実行時エラーが起きないようにするには、引数型は反変だけが許されるべきです。そして、もし共変ならコンパイルエラーで知らせてほしいところです。ところが、TypeScriptは引数型は双変(つまり共変もOK)であるため、安心できない仕様になっています。
引数の共変性を許さないstrictFunctionTypes
上の課題を解決するのが、コンパイラオプションstrictFunctionTypesです。これをtrueにすると、引数が反変になります。もし、共変の引数にした場合、TypeScriptが警告を出します。
tsletfunc : (n : number | null) => any;// 不変func = (n : number | null) => {}; // OK// 反変func = (n : number | null | undefined) => {}; // OK// 共変Type '(n: number) => void' is not assignable to type '(n: number | null) => any'. Types of parameters 'n' and 'n' are incompatible. Type 'number | null' is not assignable to type 'number'. Type 'null' is not assignable to type 'number'.2322Type '(n: number) => void' is not assignable to type '(n: number | null) => any'. Types of parameters 'n' and 'n' are incompatible. Type 'number | null' is not assignable to type 'number'. Type 'null' is not assignable to type 'number'.= ( func n : number) => {}; // NG
tsletfunc : (n : number | null) => any;// 不変func = (n : number | null) => {}; // OK// 反変func = (n : number | null | undefined) => {}; // OK// 共変Type '(n: number) => void' is not assignable to type '(n: number | null) => any'. Types of parameters 'n' and 'n' are incompatible. Type 'number | null' is not assignable to type 'number'. Type 'null' is not assignable to type 'number'.2322Type '(n: number) => void' is not assignable to type '(n: number | null) => any'. Types of parameters 'n' and 'n' are incompatible. Type 'number | null' is not assignable to type 'number'. Type 'null' is not assignable to type 'number'.= ( func n : number) => {}; // NG
strictFunctionTypesは思いがけない実行時エラーを防ぐのに役立ちます。strictFunctionTypesはtrueを設定するのがお勧めです。
メソッド型はチェックされない
strictFunctionTypesのチェックが働くのは関数型だけです。メソッド型には働きません。
tsinterfaceObj {// メソッド型method (n : number | null): any;}constobj :Obj = {method : (n : number) => {}, // チェックされない};
tsinterfaceObj {// メソッド型method (n : number | null): any;}constobj :Obj = {method : (n : number) => {}, // チェックされない};
インターフェースのメソッドでも、関数型で定義されたメソッドはstrictFunctionTypesのチェックが働きます。
tsinterfaceObj {// 関数型method : (n : number | null) => any;}constobj :Obj = {Type '(n: number) => void' is not assignable to type '(n: number | null) => any'. Types of parameters 'n' and 'n' are incompatible. Type 'number | null' is not assignable to type 'number'. Type 'null' is not assignable to type 'number'.2322Type '(n: number) => void' is not assignable to type '(n: number | null) => any'. Types of parameters 'n' and 'n' are incompatible. Type 'number | null' is not assignable to type 'number'. Type 'null' is not assignable to type 'number'.: ( method n : number) => {}, // チェックが働く};
tsinterfaceObj {// 関数型method : (n : number | null) => any;}constobj :Obj = {Type '(n: number) => void' is not assignable to type '(n: number | null) => any'. Types of parameters 'n' and 'n' are incompatible. Type 'number | null' is not assignable to type 'number'. Type 'null' is not assignable to type 'number'.2322Type '(n: number) => void' is not assignable to type '(n: number | null) => any'. Types of parameters 'n' and 'n' are incompatible. Type 'number | null' is not assignable to type 'number'. Type 'null' is not assignable to type 'number'.: ( method n : number) => {}, // チェックが働く};
学びをシェアする
⚙️TypeScriptのstrictFunctionTypesは、引数型の変性のチェックを厳しくするコンパイルオプション
☹️TypeScriptの引数は双変で安心できない
🔥実行時エラーが起こることも
✅strictFunctionTypesは反変にしてくれる
👍有効化推奨のオプション
『サバイバルTypeScript』より
関連情報
📄️ strict
strict系のオプションを一括で有効化する