generated at
TypeScriptのArrayはinvariantではなくcovariantなので健全性が壊れている


ts
type Animal = Cat | Dog; type Cat = 'cat'; type Dog = 'dog'; let cats: Cat[] = ['cat']; let animals: Animal[] = cats; animals[0] = 'dog'; // cats[0]とanimals[0]の中身が変わる console.log(cats[0]); // => "dog"
最終行
cats の型は Cat[] のはずなのに、値を見ると 'dog' になっている
invariantであるならば、 let animals: Animal[] = cats; の部分でerrorが生じる
例えば、Scalaで同じコードを書いた例を見ると良い ref
ちなみに、mrsekutの場合はmutableな操作を一切書かないので、これが問題になったことはない


v4.7で入ったOptional Variance Annotations for Type Parametersを使うと正しくできる
ts
interface InvariantArray<in out T> extends Array<T> {} type Animal = Cat | Dog; type Cat = 'cat'; type Dog = 'dog'; let cats: InvariantArray<Cat> = ['cat']; let animals: InvariantArray<Animal> = cats; // error animals[0] = 'dog'; console.log(cats[0]);
builtinの Array 型は依然として壊れているが、それを拡張した型を定義すれば型安全にできる
上述したScalaと同じ位置で型errorを出せているmrsekut




immutableであることを最初から前提にするなら
TypeScriptのreadonlyなり、ReadonlyArray<T>なりを使うという方法もある
ts
let cats: readonly Cat[] = ['cat']; let animals: readonly Animal[] = cats; animals[0] = 'dog'; // error console.log(cats[0]);
varianceの話ではないので、errorが生じる位置が異なることに注意mrsekut