型の互換性
型の互換性(Type Compatibility)
型の互換性は、あるものを別のものに割り当てることができるかどうかを決定します。例えばstringとnumberは互換性がありません:
let str: string = "Hello";
let num: number = 123;
str = num; // ERROR: `number` is not assignable to `string`
num = str; // ERROR: `string` is not assignable to `number`健全性(Soundness)
TypeScriptの型システムは便利であるように設計されているため、不健全な振る舞いをすることも可能にしています。例えば、何かをany型にすることができます。これは、あなたが望むことを何でもできるようにすることをコンパイラに指示することを意味します:
let foo: any = 123;
foo = "Hello";
// Later
foo.toPrecision(3); // Allowed as you typed it as `any`構造的(Structual)
TypeScriptオブジェクトは構造的に型付けされています。つまり、型名は構造が一致する限り重要ではありません
これにより、(素のJSのように)すばやくオブジェクトを作成することができ、推論が可能な場合でも安全性が保たれます。
また、より多くのデータが存在することは問題なしとされます:
バリアンス(Variance)
バリアンスは、型の互換性分析のために理解しやすく、重要な概念です。
単純な型BaseとChildの場合、ChildがBaseの子であれば、ChildのインスタンスはBase型の変数に代入することができます。
これはポリモーフィズム101です
このようなBase型とChild型で構成される複合型の型互換性は、類似のシナリオにおけるBaseとChildがバリアンス(variance)によって制御されることに依存しています。
コバリアント(Covariant):(coはjointの意味)同じ方向のみ
コントラバリアント(Contravariant):(contraはnegativeの意味)反対の方向にのみ
バイバリアント(Bivariant):(biはbothの意味)coとcontraの両方。
インバリアント(Invariant):型がまったく同じでない場合、それらは互換性がありません。
注意:JavaScriptのようなミュータブル(変更可能)なデータの存在下で、完全に健全な型システムのためには
invariantが唯一有効なオプションです。しかし、述べたように利便性によって、私たちは不健全な選択をせざるを得ません。
関数(Functions)
2つの関数を比較するときには、いくつか些細なことを考慮する必要があります。
戻り値の型(Return Type)
covariant:戻り型は少なくとも十分なデータを含んでいなければなりません。
引数の数
引数が少ないのは問題ありません(つまり、関数は追加のパラメータを無視することができます)。少なくとも十分な引数で呼び出されることが保証されています。
オプションおよび可変長(Rest)パラメータ
(事前に決定された数の)オプションパラメータおよび可変長パラメータ(任意の数)は、(繰り返しになりますが)利便性のために互換性があります。
注意:strictNullChecksがfalseの場合、オプション引数(ここでは
bar)とオプションではない引数(この例ではfoo)のみ互換性があります。
引数のタイプ
bivariant:これは一般的なイベント処理のシナリオをサポートするように設計されています
また、Array<Child>をArray<Base>(Covariance)に関数として割り当てることもできます。配列のCovarianceはすべてのArray<Child>関数がArray<Base>に代入可能であることを必要とします。例えばpush(t:Child)は関数パラメータのBivarianceによってpush(t:Base)に代入可能です。
これは他の言語から来た人はエラーになると予測するかもしれません。**混乱を招く可能性がありますが、**これはTypeScriptではエラーになりません:
列挙型(Enums)
列挙型は数値と互換性があり、数値は列挙型と互換性があります。
異なる列挙型のEnum値は互換性がないとみなされます。これによって、列挙型を(構造的にではなく)nominalに使うことができます
クラス(Classes)
インスタンスメンバとメソッドのみが比較されます。コンストラクタと静的メンバに関しては何もしません。
privateとprotectedメンバは同じクラスで書かれたものでなければなりません。そのようなメンバは、本質的にそのクラスをnominalにします。
ジェネリクス(Generics)
TypeScriptは構造的な型システムを備えているため、型パラメータはメンバが使用するときの互換性にのみ影響します。例えば、以下のTは互換性に影響を与えません:
しかし、Tを使用すると、以下に示すようにインスタンス化に基づいて互換性チェックの働きをします。
ジェネリック引数がインスタンス化されていない場合、それらは互換性をチェックする前にanyで置き換えられます:
クラスを含むジェネリックは、前述のように関連するクラスの互換性によってマッチされます。例:
ノート: Invariance
私たちは不変性が唯一の健全な選択肢だと言いました。ここでは、contraとcoのVarianceの両方が配列に危険である例を示します。
最終更新
役に立ちましたか?