`any` extend `never`?

この記事は[ZOZO Advent Calendar 2023](https://qiita.com/advent-calendar/2023/zozo) シリーズ3、2日目の記事です。

rimrafが有名だったり最近ではtsimpが話題になったisaacs氏が先月、Xに以下のようなポストをしました。

https://x.com/izs/status/1720223437380370942

> TypeScriptクイズ！
> 
> 調べずに、anyはneverを拡張しますか？
> 
> - はい
> - いいえ
> - はいもいいえも両方
> - どちらもない

(ChatGPT訳)

TypeScriptを読み書きしていると、型表現のなかで `A extends B` といった定義を見かけることがあります。
ジェネリクスの制約や、Conditional Typesなどが主でしょう。
（JavaScriptのクラスの継承は今回の話の対象ではないので省略します）

さて、話は戻って上のクイズ、解けましたか？
クイズのように急に言われると、どのように考えればよいか戸惑う方も多いのではないでしょうか。

`no` の回答が最も多いようですが答えは `both yes and no` です。

この記事ではTypeScriptの `extends` をどのように考えれば良いかを考えてみます。

## does \`any\` extend \`never\`?

TypeScriptにおける `extends` は型の大小関係を測るようなものです。

上の *does \`any\` extend \`never\`?* をコードに落とし込んでみましょう。

```ts
type AnyExtendNever = any extends never ? true : false;
```
_[TypeScript Playground](https://www.typescriptlang.org/play?#code/C4TwDgpgBAggdiAogD2BOATAchAbhAJygF4oBDBKCVdDAZyjj0KgH4pgCBXaALigBmZADZ0IAbgBQAemlR5APVaSgA)_

この時点で `AnyExtendNever` の型が想像つく方は、この記事の内容は読まなくても大丈夫です。
このままではわからないので関数のジェネリクスの形で考えてみます。

```ts
function fn1<T extends never>(_: T) { ... }
function fn2<T extends never>(): T { ... }
```

さて二つに増えました。これは入出力それぞれで考える必要があるためです。

まずは `fn1` のケースで考えます。
引数に `any` な値を渡して関数を呼べば *does \`any\` extend \`never\`?* の検証ができそうです。

```ts
function fn1<T extends never>(_: T) {}
declare const anyValue: any;
fn1(anyValue);
```

_[TypeScript Playground](https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABMMBGAPAFUQUwB5Q5gAmAzomDgG44BOAfABQD6AXIpgJSIDeAvgChiOCABsAhrRyIICUlETiwATwBq40SBzslygNwCUqRrvWacnPUA)_

この時、 `Argument of type 'any' is not assignable to parameter of type 'never'.` のように型エラーになります。
*does \`any\` extend \`never\`?* に対して `no` と見ることができそうです。

次に `fn2` のケースで考えます。
`any` な値をreturnすることで検証ができそうです。

```ts
declare const anyValue: any;
function fn2<T extends never>(): T {
    return anyValue;
}
```

_[TypeScript Playground](https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEYD2A7AzgF3lFBPAalBAK4gBc2eA3AFABmxKYGAlqvHSgIwA8AKvBAAPDCBTA08FCABuIGAD4AFAEoKAgN414O+HAzEYKSgSKlaAXyA)_

これはエラーになりません。
つまり *does \`any\` extend \`never\`?* に対して `yes` と見ることができそうです。

`yes` と `no` 、両方が出てきました。
つまり答えとしては *does \`any\` extend \`never\`?* は yesともnoとも言える `both yes and no` となるわけです。

> Without looking it up

調べるなと言われていますが、今回はPlaygroundを用いつつ検証しました。
なので次は想像してみましょう。

## does \`never\` extend \`any\`?

https://twitter.com/izs/status/1720223438630236612

先の逆パターンです。
得票は `no` が最多ですが、ほんとうにそうでしょうか。

考え方は同じで関数の入出力を想像します。

```ts
function fn1<T extends any>(_: T) { ... }
function fn2<T extends any>(): T { ... }
```

`fn1` には `never` な値を渡すことを考えてみます。

```ts
function fn1<T extends any>(_: T) {}
declare const neverValue: never;
fn1(neverValue);
```

このとき関数は `function fn1(_: any) {}` のように見ることもできます。
`any` な変数には `never` な値も代入できるので、これは型エラーにならなそうです。
つまり *does \`never\` extend \`any\`?* に対して `yes` と見ることができます。

`fn2` では `never` な値をreturnすることを考えます。

```ts
declare const neverValue: never;
function fn2<T extends any>(): T {
    return neverValue;
}
```

このとき関数は `function fn2(): any { return neverValue; }` のように見ることもできます。
`any` な値が返せるなら `never` な値も返せるので、これも型エラーにならなそうです。
つまり *does \`never\` extend \`any\`?* に対して `yes` と見ることができます。

どちらのパターンにおいても `yes` と言えるので、
*does \`never\` extend \`any\`?* に対して `yes` と答えられます。

## 終わりに

今回のように `any` と `never` を実務などで用いることは多くないと思います。
ですがTypeScriptの型と仲良くなるための一歩にはなると思います。

`any` や `never` の部分を `unknown`, `number`, `123`, `{a:1}`, `Record<string, never>` など色々と変えてみて遊んでみてください。