TypeScript Array.prototype.every の型推論

配列のeveryを使うときに、型を狭めてほしいところで、TypeScriptがうまく推論してくれないことがあります。

const numbersOrStrings: (number | string)[] = [1, 2, 3, 4];

if(numbersOrStrings.every(v => typeof(v) === 'number')) {
    const _numbers: number[] = numbersOrStrings; // Type '(string | number)[]' is not assignable to type 'number[]'.
    // ...
}

numbersOrStrings は number または string の配列です。

これを every を使ってすべての要素が number であるかをチェックしています。

普通に考えると if が true ならすべての要素が number であることが期待されますが、TypeScript は string | number のままとします。

これを回避したい場合は、Type ガードを使います。

const isNumber = (x: unknown): x is number => typeof(x) === "number"

const numbersOrStrings: (number | string)[] = [1, 2, 3, 4];

if(numbersOrStrings.every(isNumber)) {
    const _numbers: number[] = numbersOrStrings;
    // ...
}

TypeScript 3.X のときは上記のように Type ガードを使ってもエラーになっていました。

4.X からType ガードを使った際に型が狭められるようになったようです。

以下がPRです。

https://github.com/microsoft/TypeScript/pull/38200