異なる型の配列の型を指定したい場合はtupleを使ったほうが良いかも

handbookやspecに書いてあることではあるけれど。

はじめに

例えば以下の様な定義があるとする。

class X {
  say() {
    return "x";
  }
}

class Y {
}

ここで以下のような型は型チェックを通らない。正しい。

let y: Y = new Y();
y.say();

もちろん以下のようなコードは正しい。

let x: X = new X();
y.say();

ここで異なる型の配列を指定したい場合がある*1。このような時はどうすれば良いかという話。

var values = [new X(), new Y()]
values[0].say();

方法

any

一番単純なのはany型で定義して、利用する際にcastすることかもしれない。まぁ良くない。

// this is bad.
let values: any[] = [new X(), new Y()];
console.log((<X>values[0]).say());

union type

次に考えるのはunion typeを使った配列の方として扱う方法。少なくとも、XかYのどちらかの型になることが保証される。 ただ、結局のところany[]型で定義するのと変わらない。

// this is not good
let values: (X | Y)[] = [new X(), new Y()];
console.log((<X>values[0]).say());

tuple

こういう時はtupleを使うのが良さそう。tupleというのはusecaseそのもの。

let values: [X, Y] = [new X(), new Y()];

// 順序が異なればエラーになる
// let values: [Y, X] = [new X(), new Y()]; // compile error

console.log(values[0].say());

// もちろんYはsay()を持っていないのでエラーになる。
// console.log(values[1].say()); // compile error

memo: 可変長のtupleはissueにはなっているっぽい。

以下のようにも書きたいと言うことがあるかもしれない。可変長のtuple。これはissueにはなっているっぽい。

let values: [X, ...Y] = [new X(), new Y()];

参考

*1:例えば、angular.jsのdirectiveを定義した際のcontrollersの部分など