## 事象
以下のコードが[[型述語推論 (TypeScript)|型述語推論]]されない。
```ts
declare let numberOrUndefines: (number | undefined)[];
const numbers = numberOrUndefines.filter((x) => !!x);
// ^? (number | undefined)[]
```
`x = undefined`の場合は`!!x`が`false`になるため、`number[]`に推論されてほしい。
## 原因
[[アロー関数 (JavaScript)|アロー関数]] `(x) => !!x` は[[型述語 (TypeScript)|型述語]]の定義を満たさないから。[[型述語 (TypeScript)|型述語]]の定義は以下。
> 関数 `function(x)` が以下2つの条件を満たすとき、`function(x): x is T` と表現できる。
>
> 1. もし関数が`true`を返却するなら、`x`は`T`型である
> 2. もし関数が`false`を返却するなら、`x`は`T`型ではない
これを検証する。
#### 条件1について
> 1. もし関数が`true`を返却するなら、`x`は`number`型である
`x`が`undefined`を返す場合、この関数の戻り値は`false`になる。以上より、`x`は`number | undefined`であるため、**関数が`true`を返却するなら、`x`は`number`型である**と言える。つまり、**条件1を満たす**。
#### 条件2について
> 2. もし関数が`false`を返却するなら、`x`は`number`型ではない
対偶として『`x`が`number`型であるなら、関数は`true`を返却する』を考える。
`x`が`number`型であったとしても、`x = 0`のときは関数は`false`を返す。よって、対偶条件は成立しないため、元の**条件2も成立しない**。
## 解決方法
`(x) => x !== undefined` や `(x) => x != null` のような[[アロー関数 (JavaScript)|アロー関数]]に変更する。
`x = 0`のときでも`0 !== undefined`や`0 != null`は成立するため2つの条件を満たし、[[アロー関数 (JavaScript)|アロー関数]]は[[型述語推論 (TypeScript)|型述語推論]]される。
## 参考
- [Documentation - TypeScript 5.5](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html#inferred-type-predicates)