strictFunctionTypes
strictFunctionTypes
は引数型の変性のチェックを厳しくするコンパイラオプションです。
- デフォルト: strictが有効の場合は
true
、それ以外はfalse
- 追加されたバージョン: 2.6
- TypeScript公式が有効化推奨
引数の双変性は安心できない
TypeScriptの関数には引数の双変性(parameter bivariance)という性質があります。どういうことか、順を追って見ていきましょう。
まず、次の3つの型の範囲を考えてみましょう。
number
number | null
number | null | undefined
number
はnumber | null
より狭い型です。number | null
の範囲には1
や0.5
などの数値型とnull型があります。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
を取る関数です。
ts
letfunc : (n : number | null) => any;
ts
letfunc : (n : number | null) => any;
この変数func
に代入できる値はどんな型でしょうか。当然、型注釈と同じ関数は問題なく代入できます。
ts
func = (n : number | null) => {}; // OK
ts
func = (n : number | null) => {}; // OK
引数number | null
より広いnumber | null | undefined
を受ける関数は代入できるでしょうか。これも大丈夫です。
ts
func = (n : number | null | undefined) => {}; // OK
ts
func = (n : number | null | undefined) => {}; // OK
このような引数型の範囲を広められる特性を引数の反変性(parameter contravariance)と言います。
引数number | null
より狭いnumber
を取る関数は代入できるでしょうか。これもTypeScriptでは代入できます。
ts
func = (n : number) => {}; // OK
ts
func = (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が警告を出します。
ts
letfunc : (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
ts
letfunc : (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
のチェックが働くのは関数型だけです。メソッド型には働きません。
ts
interfaceObj {// メソッド型method (n : number | null): any;}constobj :Obj = {method : (n : number) => {}, // チェックされない};
ts
interfaceObj {// メソッド型method (n : number | null): any;}constobj :Obj = {method : (n : number) => {}, // チェックされない};
インターフェースのメソッドでも、関数型で定義されたメソッドはstrictFunctionTypes
のチェックが働きます。
ts
interfaceObj {// 関数型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) => {}, // チェックが働く};
ts
interfaceObj {// 関数型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』より