TypeScript 共変と反変について

Animal型とDog型があるとします。AnimalはDogのスーパークラスのような位置付けです。
type Animal = 'Dog' | 'Cat' | 'Tami'
type Dog = 'Dog'
イメージ的には、AnimalはDogを含みます。
そのため、以下のようにAnimal型にDog型を代入することはできますが、その逆はできません。
declare let myDog: Dog
declare let myAnimal: Animal
const animal: Animal = myDog //ok
const dog: Dog = myAnimal //ng
このようにAnimal型とDog型の関係と同じように動作するのが共変です。
Animal型はDog型を含む(広い範囲を指す)。なので、Animal型にDog型を代入できる。
一方で、反変というものがあり、これはAnimal型がDog型のスーパークラスなら、その逆の関係になることをいいます。
type AnimalFunc = (x: Animal) => void
type DogFunc = (x: Dog) => void
const a: AnimalFunc = (x: Dog) => console.log('dog') //ng
const d: DogFunc = (x: Animal) => console.log('animal') //ok
AnimalFunc型に対してDogFuncを入れようとするとエラーになります。(strictFunctionTypesがtrueの場合に限る)。
先ほどのイメージだと、AnimalFuncはDogFuncを含みそう(より広いイメージ)なので、なぜエラーになるのか?と疑問に思います。
しかし、対象が関数の引数だった場合は、逆の関係になります。
AnimalFuncは引数にAnimalを受け取って何か処理する関数です。
つまり、Dog、Cat、Tamiのどれがきても処理がされることを想定されています。
一方で、DogFuncは引数にDogだけを受け取ります。
そのため、AnimalFunc型を想定している変数にDogFunc を入れてしまうと、使う側としては(上記の例の場合、変数aの関数を実行しようとする時に)CatやTamiも引数に渡す可能性があるので、そのときにDogFuncでは処理がしきれない、という状況になります。
そのため、strictFunctionTypesがtrueの場合には、関数の引数は反変としてエラーにしています。
最近のコメント