in / out 共変性と反変性
最終更新:2026-10-22
in / out 共変性と反変性
型変は「複合型の中の型パラメータが全体の互換性にどう影響するか」を記述します。戻り値は共変(サブタイプで置き換え可能)、パラメータは反変(親型を受け取る関数で置き換え可能)。関数代入の互換性、配列やインターフェースのネストした互換性はすべてこれで決まります——理解すれば「なぜこの代入がエラーになる/ならないのか」に体系的な答えが得られます。
| グループ | ジェネリクス |
|---|---|
| 種類 | 構文 |
| strict モード | 不要 |
ℹ️ 説明 エディタは TypeScript を JavaScript にトランスパイルして実行します(型チェックなし)。型チェック結果は各サンプルの出力を参照してください。
📝 構文
// TS 4.7 からは型変異を明示できる(デフォルトではコンパイラーが構造的に推論する)
interface Producer<out T> { produce(): T; } // 共変:T を作り出すだけ
interface Consumer<in T> { consume(v: T): void; } // 反変:T を消費するだけ
// 関数代入の自然な規則
let handleDog: (d: Dog) => void = handleAnimal; // OK:引数は反変
const adoptAnimal: (tag: string) => Animal = adoptDog; // OK:戻り値は共変
⚙️ パラメータ
| <out T> | out(共変):T が出力位置(戻り値)にのみ現れます。Producer<Dog> は Producer<Animal> として使えます |
|---|---|
| <in T> | in(反変):T が入力位置(パラメータ)にのみ現れます。Consumer<Animal> は Consumer<Dog> として使えます |
| <in out T> | in out(不変):読み書きの両方が開いています。厳密に同型である必要があり、そうでなければ互換になりません |
戻り値:型変は型の互換性ルールで、実行時コードを生成しません。決めるのは「誰を誰に代入できるか」だけです。
▶ サンプル
下のコードを編集して「試す」を押すと、トランスパイル実行結果を確認できます:
出力:
Rex eats food
Rex-A1 eats food
(トランスパイル後の実行出力。逆方向は成立しません——Dog しか処理できない関数を (a: Animal) => void の枠に代入するとコンパイラに拒否されます。まさにパラメータの反変が安全でない代入を止めているのです)
💡 ポイント
ℹ️ 説明 直観的な覚え方:「産出はより具体的でよく(共変)、消費はより広くてよい(反変)」——Animal を受け入れる飼い主は当然 Dog も世話でき、Dog を産出する犬舎は Animal を求める誰にでも供給できます。
⚠️ 注意 可変配列は産出も消費も行う(双変)ため、安全でない代入の多発地帯です。ジェネリックインターフェースを設計するときは、まず T が入るのか出るのかを考え、それから in/out を付けましょう。
💡 ヒント TS 4.7 の明示的な注釈は主にドキュメント化と高速化のためのものです。構造的な推論はたいてい正しいのですが、in/out を付けると深い型検査が速くなり、意図が明確になり、エラーメッセージも素直になります。
🏷️ 関連ジェネリクス
💡 関連ケース
ケースは今後追加予定です。
📚 関連チュートリアル
❓ よくある質問
Q 共変と反変を一言で言い分けると?
A 型パラメータがどこに現れるかを見ます。出力位置(戻り値)は共変——サブタイプでも全体として互換。入力位置(パラメータ)は反変——親タイプでも全体として互換。入力でもあり出力でもあるなら不変(厳密に同型)です。
Q 関数パラメータが共変ではなく反変なのはなぜですか?
A 反例で示せます。(d: Dog) => void を (a: Animal) => void に代入できてしまうと、呼び出し側が Cat を渡した瞬間に壊れます。逆に (a: Animal) => void を (d: Dog) => void に代入するのは安全です——どんな Animal も処理できる関数は、当然 Dog も処理できます。
Q いつ手書きの in/out 注釈が必要ですか?
A 型パラメータの位置が複雑なとき、構造的な推論が遅いとき、結果が直観に反するとき(イベント emitter、深くネストしたインターフェースなど)。日常の業務コードはデフォルトの構造的推論に任せればよく、あちこちで注釈する必要はありません。