TypeScriptのArrayはinvariantではなくcovariantなので健全性が壊れている
#TypeScript型システムの健全性の破壊
例
code:ts
type Animal = Cat | Dog;
type Cat = 'cat';
type Dog = 'dog';
let cats: Cat[] = 'cat';
let animals: Animal[] = cats;
animals0 = 'dog'; // cats0とanimals0の中身が変わる
console.log(cats0); // => "dog"
最終行
catsの型はCat[]のはずなのに、値を見ると'dog'になっている
invariantであるならば、let animals: Animal[] = cats;の部分でerrorが生じる
例えば、Scalaで同じコードを書いた例を見ると良い ref
ちなみに、mrsekut.iconの場合はmutableな操作を一切書かないので、これが問題になったことはない
v4.7で入ったOptional Variance Annotations for Type Parametersを使うと正しくできる
code: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
animals0 = 'dog';
console.log(cats0);
builtinのArray型は依然として壊れているが、それを拡張した型を定義すれば型安全にできる
上述したScalaと同じ位置で型errorを出せているmrsekut.icon
immutableであることを最初から前提にするなら
TypeScriptのreadonlyなり、ReadonlyArray<T>なりを使うという方法もある
code:ts
let cats: readonly Cat[] = 'cat';
let animals: readonly Animal[] = cats;
animals0 = 'dog'; // error
console.log(cats0);
varianceの話ではないので、errorが生じる位置が異なることに注意mrsekut.icon
https://github.com/microsoft/TypeScript/issues/1394