unknown, any, never と extends の関係のメモ

`Conditional Types`(`X extends Y ? A : B`)で
`unknown` や `any` が絡んでハマってしまったので
その際に調べた内容をメモとして残しておきます。

## 調べたこと

`X extends Y` において `X` や `Y` に`any`, `unknown` が登場したときに
どのような判定になるかを調べてみました。
あとついでに`never` もいれてみた

`TypeScript` のバージョンは`3.9.7` で検証しました。
型推論の結果確認は VSCode 上でフォーカスをあてて確認しました。

## コード

判定用のコードです。

```ts
type spu1 = unknown extends string ? "〇" : never;
type spu2 = unknown extends number ? "〇" : never;
type spu3 = unknown extends unknown ? "〇" : never;
type spu4 = unknown extends any ? "〇" : never;
type spu5 = unknown extends never ? "〇" : never;
type spu6 = unknown extends null ? "〇" : never;
type spu7 = unknown extends undefined ? "〇" : never;
type spu8 = unknown extends symbol ? "〇" : never;
type spu9 = unknown extends {} ? "〇" : never;

type sbu1 = string extends unknown ? "〇" : never;
type sbu2 = number extends unknown ? "〇" : never;
type sbu3 = unknown extends unknown ? "〇" : never;
type sbu4 = any extends unknown ? "〇" : never;
type sbu5 = never extends unknown ? "〇" : never;
type sbu6 = null extends unknown ? "〇" : never;
type sbu7 = undefined extends unknown ? "〇" : never;
type sbu8 = symbol extends unknown ? "〇" : never;
type sbu9 = {} extends unknown ? "〇" : never;

//---
type spa1 = any extends string ? "〇" : never;
type spa2 = any extends number ? "〇" : never;
type spa3 = any extends unknown ? "〇" : never;
type spa4 = any extends any ? "〇" : never;
type spa5 = any extends never ? "〇" : never;
type spa6 = any extends null ? "〇" : never;
type spa7 = any extends undefined ? "〇" : never;
type spa8 = any extends symbol ? "〇" : never;
type spa9 = any extends {} ? "〇" : never;

type sba1 = string extends any ? "〇" : never;
type sba2 = number extends any ? "〇" : never;
type sba3 = unknown extends any ? "〇" : never;
type sba4 = any extends any ? "〇" : never;
type sba5 = never extends any ? "〇" : never;
type sba6 = null extends any ? "〇" : never;
type sba7 = undefined extends any ? "〇" : never;
type sba8 = symbol extends any ? "〇" : never;
type sba9 = {} extends any ? "〇" : never;

//---
type spn1 = never extends string ? "〇" : never;
type spn2 = never extends number ? "〇" : never;
type spn3 = never extends unknown ? "〇" : never;
type spn4 = never extends any ? "〇" : never;
type spn5 = never extends never ? "〇" : never;
type spn6 = never extends null ? "〇" : never;
type spn7 = never extends undefined ? "〇" : never;
type spn8 = never extends symbol ? "〇" : never;
type spn9 = never extends {} ? "〇" : never;

type sbn1 = string extends never ? "〇" : never;
type sbn2 = number extends never ? "〇" : never;
type sbn3 = unknown extends never ? "〇" : never;
type sbn4 = any extends never ? "〇" : never;
type sbn5 = never extends never ? "〇" : never;
type sbn6 = null extends never ? "〇" : never;
type sbn7 = undefined extends never ? "〇" : never;
type sbn8 = symbol extends never ? "〇" : never;
type sbn9 = {} extends never ? "〇" : never;
```

## 結果

以下のような結果になりました。

### `unknown`

| type      | unknown extends T | T extends unknown |
| --------- | :---------------: | :---------------: |
| string    |                   |        〇         |
| number    |                   |        〇         |
| unknown   |        〇         |        〇         |
| any       |        〇         |        〇         |
| never     |                   |        〇         |
| null      |                   |        〇         |
| undefined |                   |        〇         |
| symbol    |                   |        〇         |
| {}        |                   |        〇         |

### `any`

| type      | any extends T | T extends any |
| --------- | :-----------: | :-----------: |
| string    |      〇       |      〇       |
| number    |      〇       |      〇       |
| unknown   |      〇       |      〇       |
| any       |      〇       |      〇       |
| never     |      〇       |      〇       |
| null      |      〇       |      〇       |
| undefined |      〇       |      〇       |
| symbol    |      〇       |      〇       |
| {}        |      〇       |      〇       |

### `never`

| type      | never extends T | T extends never |
| --------- | :-------------: | :-------------: |
| string    |       〇        |                 |
| number    |       〇        |                 |
| unknown   |       〇        |                 |
| any       |       〇        |       〇        |
| never     |       〇        |                 |
| null      |       〇        |                 |
| undefined |       〇        |                 |
| symbol    |       〇        |                 |
| {}        |       〇        |                 |

上記のような結果になりました。

めでたしめでたし 😀

## ジェネリックを使うと`never`の部分で結果が変わる

上記の調査では型をハードコーディングしていますが、
最初はジェネリックを使って以下のようなコードで検証していました。

```ts
type IsSuperUnknown<T> = unknown extends T ? "〇" : never;
type IsSubUnknown<T> = T extends unknown ? "〇" : never;

type IsSuperAny<T> = any extends T ? "〇" : never;
type IsSubAny<T> = T extends any ? "〇" : never;

type IsSuperNever<T> = never extends T ? "〇" : never;
type IsSubNever<T> = T extends never ? "〇" : never;

//検証コード
type spu1 = IsSuperUnknown<string>;
type spu2 = IsSuperUnknown<number>;
//以下省略
```

ところがこのコードだと 以下のような現象が起きました。

```ts
//never extends unknown
type sbu5 = IsSubUnknown<never>; //never
type spn3 = IsSuperNever<unknown>; //〇

//never extends any
type sba5 = IsSubAny<never>; //never
type spn4 = IsSuperNever<any>; //〇
```

試しに変数に代入してみます

```ts
let vsbu5: sbu5 = "〇"; //コンパイルエラー
let vspn3: spn3 = "〇";

let vsba5: sba5 = "〇"; //コンパイルエラー
let vspn4: spn4 = "〇";
```

この挙動は何なんでしょう...
仕様なのかな？🤔

## おわりに

可視化をすることで理解を深めることができました。
こういった検証も大事なんだなとあらためて実感しました😀

あとジェネリック使った時の結果は何なんでしょうか...