TypeScriptの型パズル初級問題の解説

TypeScriptの型パズル初級の解答例を見て、なぜこのコードでこのエラーなのか考えていたら
私にはとてもいろんなことが学びになったので、共有します。
それではまず、TypeScriptの型パズル初級の問題を見てください。

# TypeScriptの型パズル初級問題の内容
[画像の性質を持つ型 RequireEither を作ってください。(TypeScript v3.7.5)](https://twitter.com/ryo4429399/status/1224681166717874177?s=20)
![スクリーンショット 2020-02-07 15.26.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384722/7f3bce31-0d16-cdcb-b46e-41e0f27530bd.png)

こちらは[りょーさん](https://twitter.com/ryo4429399)が作成した問題です。(掲載許可は事前にとりました)

問題を解くためには、まずどんな性質を持つ型を作ればいいのか理解していないと無理です。

# RequireEitherはどんな性質の型なのか

if文による型の絞り込み結果を見たらわかります。

```typescript
func({}); // Error
func({ foo: 42 }); // 'foo' in hogeの時に、{foo: number}を返している。
func({ bar: 'some string' }); // 'bar' in hogeの時に、{bar: string}を返している。
func({ baz: true }); // 'baz' in hogeの時に、{baz: boolean}を返している。
func({ hogehoge: true }); // Error
```

このことから、RequireEitherという型は`foo | bar | baz`の性質を持つ型ということがわかりますね。

それをどう実現するのか私には、理解できなかったので大人しく解答例を見ました。

# 解答例
以下解答例です。([ Playgroundで見たい方はこちら](https://www.typescriptlang.org/play/index.html?ssl=34&ssc=26&pln=30&pc=1#code/C4TwDgpgBAShCOBXAlgJwgUWcAFhVAPAAqoD2YAzgDRQDSEIFUEAHsBAHYAmTA1g6QBmUEuSYBeKPxBCRZSgD4o4gFBR1dBk1btufAcNGUoAfigBvKAG1aUZB02MAugC45Ym06gBfKG44QAG74ANwqKqCQUAASpADm0JJwSGiY2HiE5moagqSk-ogAtgBGoerFAIaobhTAqPZxIeUVAF5uxXkANhAVHGHeCmEqgogcAMbAyKQOI+MAFDjxEG6xCQCUFtnq3cBQHAUloVtQO1AUNXUNYRonELvF7V09fcdj07UWULmkylAADDRKqhfgByEGA1q-QQVToUaC+SSLBLXDTIYRzEHfEF2BxIiAbLI3G4ORFLAB03yaZ1+eLJQKpxRp5MqLRR6m8xzRUAxQOx9igeIJx2JTISFLyVIkAuZVQZoogdNabJ8nPRIJZfNxSyFRI0JOlYsp6iltPp5XlitZxw5Ny5GKxOIN0AAZM6oOqqpqnTrdXsLUbqaSxWaoIygwqWcqbai1Q7+XioAAfRPu3mOwWbX1+8Pi0iSi0hsNOy1RlQc4ajMZzczeNZhWZVyzfNwAFgATD46xX5pYgW4QRRSIVoLV6hw4tja-XK9XQ603HVEPCuw3Z3i8QvUEvO0MgA))

```typescript
type RequireEither<Props, Keys extends keyof Props = keyof Props> =
    Keys extends keyof Props ? { [K in Keys]: Props[K] } : never;

type Hoge = RequireEither<{
    foo: number;  bar: string;  baz: boolean;
}>;

function func(hoge: Hoge) {
    let n: number;
    let s: string;
    let b: boolean;
    const { foo = 0, bar = '', baz = false } = hoge;
    if ('foo' in hoge) {
        n = hoge.foo;  s = hoge.bar;  b = hoge.baz;
    }
    if ('bar' in hoge) {
        n = hoge.foo;  s = hoge.bar;  b = hoge.baz;
    }
    if ('baz' in hoge) {
        n = hoge.foo;  s = hoge.bar;  b = hoge.baz;
    }
    if ('foo' in hoge && 'bar' in hoge) {
        n = hoge.foo;  s = hoge.bar;  b = hoge.baz;
    }
    if ('foo' in hoge || 'bar' in hoge) {
        n = hoge.foo;  s = hoge.bar;  b = hoge.baz;
    }
}

func({});
func({ foo: 42 });
func({ bar: 'some string' });
func({ baz: true });
func({ hogehoge: true });
```
関数のコードはなんとなく理解できても大事な、最初のコードが全く何してるかわかりませんでしたw
ここです。

```typescript
type RequireEither<Props, Keys extends keyof Props = keyof Props> =
    Keys extends keyof Props ? { [K in Keys]: Props[K] } : never;

type Hoge = RequireEither<{
    foo: number;  bar: string;  baz: boolean;
}>;
```
# 解説
以下上部コードを理解するにあたり私が知らなかったことです。

* ジェネリクス
* keyof
* Union型
* ConditionalTypes
* MappedTypes
* UnionDistribution
* LookupTypes
* IntersectionTypes

上記と共に１つずつ解説します。

## ジェネリクス
ジェネリクスとは型違いの場合、どの型でも対応できるように型引数を使い、型の抽象化をする機能です。

`Typename<T>`の形のものです。`< >`で任意の名前の列を囲うと、それらの名前を型変数として使用できます。

問題のコードでいうとこの部分です。

```typescript

RequireEither<Props, Keys extends keyof Props = keyof Props>
```

 RequireEitherという型の引数に、`Props`と `Keys extends keyof Props = keyof Props` という2つの型変数を入れています。
## keyof
`keyof T` で「type Tのプロパティ名のUnion型」を表現する型を記述できます。

(Union型については後ほど説明します。)

問題のコードでいうとこの部分ですね。

```typescript

Keys extends keyof Props = keyof Props
```

`Keys`は型引数で
`extends keyof Props`は、型引数の制約です。
`Props`のプロパティ名のUnion型を`extends`で継承しています。
`= keyof Props`は、デフォルト型引数です。

ジェネリック型の`Keys`は、`Props`のプロパティ名のUnion型を継承することになり
Union型に制約されます。

なので`RequireEither`の第二型引数`Keys`は
第一型引数の`Props`を元にデフォルトの型を計算しそれが使われます。
第二型引数を明示的に渡すこともできます。
その時は、型引数の制約つまり`extends keyof Props`を満たしているものだけになります。

(2020/02/10 「型引数の制約とデフォルト型引数を複合させた記述」というご指摘を問題作成者様から頂き追記しました。)

次にUnion型についてです。

## Union型
Union型とは、プロパティを複数の型のうちの1つにしたいとき使用します。

`T | S`のように複数の型を`|`で繋ぐ形です。`TまたはSである型`ということですね。

問題のコードのを見ると

```typescript

type Hoge = RequireEither<{
    foo: number;  bar: string;  baz: boolean;
}>;
```

`RequireEither`には`'foo'`,`'bar'`,`'baz'`の3つの型があります。

Keysは、先ほど説明した通り`Props`のプロパティ名のUnion型です。
この問題の場合、`Props`のプロパティ名のUnion型は`'foo' | 'bar' | 'baz'`という型になります。
`fooまたはbarまたはbazである型`ですね。

つまり、`keys` = `'foo'` or `'bar'` or `'baz'`ということになります。

```typescript

type RequireEither<Props, Keys extends keyof Props = keyof Props>
```

ここまで理解しました次です。

## ConditionalTypes

型レベルの条件分岐が可能な型です。
`T extends U ? X : Y`の形です。

`TがUを継承しているのならX、していないのならY`になります。

この問題でいうとここのコードです。

```typescript

Keys extends keyof Props ? { [K in Keys]: Props[K] } : never;
```

`keys`が`keyof Props`を継承しているのなら、`{ [K in Keys]: Props[K] }`、継承していないのなら`never`になります。

ですが`{ [K in Keys]: Props[K] }`がまだよくわかりません。

これがわからないと継承できた時どうなるのが正解かわからないのでまずいです。

`{ [K in Keys]: Props[K] }`はなんなのか

## MappedTypes

`{[P in K]: T}`という形の構文をMappedTypesと言います。

`P`の型変数には`in`の後ろに置かれた`Union Types`から1つずつ取り出したものが代入されていき、`P`というキーに対しての値が`T`という型なります。

(2020/02/10 `K`に対しての値が`T`という型と書いていましたが、適切ではないとご指摘いただき修正しました。)

問題のコードで置き換えると

```typescript

{ [K in Keys]: Props[K] }
```

`Keys = 'foo' | 'bar' | 'baz'`でしたね。 

`K`には、`Keys`から1つずつ取り出した`'foo'` `'bar'` `'baz'`入ります。

Keysを展開して書くと、`{[K in 'foo' | 'bar' | 'baz']: Props[K]}`となり、

MappedTypesを展開すると`{ foo: Props['foo'], bar: Props['bar'], baz: Props['baz'] }`になります。

では、`Props[K]`とは何を指しているのか？

## Lookup Types
`Props[K]`のような`T[K]`という形の構文をLookup Typesと言います。ちょうど、オブジェクト型に対してプロパティ名でアクセスするようなものを型レベルにしたようなものです。

`{ foo: Props['foo'], bar: Props['bar'], baz: Props['baz'] }`のLookup Typesを展開すると、

`{ foo: number, bar: string, baz: boolean }`になります。


しかし、問題にあった型変数を全て展開していった結果、`{ foo: number } | { bar: string } | { baz: boolean }`にはならず、元の`Props`にになってしまいました。ですが、問題の方は、実際は前者の型になるように推論されます。

それは、特殊な条件下で動作する、以下のルールが使われているからです。

## Union Distribution

Conditional Types構文のextendsの左側に書いた型が型変数であり、UnionTypesだった場合に、各要素について分配される性質です。

> 一言で説明すると、「union型の条件型」が「条件型のunion型」に変換されます。数学等ではこのような挙動をdistribution（分配）といいますから、union distributionというのもそこから来ています。
> 
> [TypeScript型初級#conditionaltypeにおけるunion distribution](https://qiita.com/uhyo/items/da21e2b3c10c8a03952f#conditional-type%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8Bunion-distribution)

問題のコードに置き換えると

```typescript
type RequireEither<Props, Keys extends keyof Props = keyof Props> =
    Keys extends keyof Props ? { [K in Keys]: Props[K] } : never;
// ConditionalTypesのextendsの左側に書いた型Keysが型変数でありUnionTypesなので
// UnionDistribution発生、Keysの各要素 'foo', 'bar', 'baz' について条件判定
('foo' extends keyof Props ? {foo: Props['foo']} : never) | ('bar' extends keyof Props ? {bar: Props['bar']} : never) | ('baz' extends keyof Props ? {baz: Props['baz']} : never)
//↓ LookupTypesにより、PropsオブジェクトのKキーに対応する値
('foo' extends keyof Props ? {foo: number} : never) | ('bar' extends keyof Props ? {bar: string} : never) | ('baz' extends keyof Props ? {baz: boolean} : never)
```

ということになります。

`Conditional Types`は、普通１つしか返さないですが、Union Distributionにより3つ全て返します。

(2020/02/10 突然UnionTypesにはならないとの指摘を受け修正しました。)


## Intersection Types

ちなみにUnion Typesと対になる`T & S & U`のような型を`Intersection Types`と言います。

複数の型を`&`で繋いで書くと、`TでありSでありUでもある`ような型で表すことができます。

問題のコードに置き換えるとここです。

```typescript
type Hoge = RequireEither<{
    foo: number;  bar: string;  baz: boolean;
}>;
```

`RequireEither={foo: number} & {bar: string} & {baz: boolean}`です。
つまり、`Props = {foo: number} & {bar: string} & {baz: boolean}`になります。

つまり、最初のコードはこうです。

```typescript
type RequireEither<Props, Keys extends keyof Props = keyof Props> =
// RequireEither<foo & bar & baz, foo | bar | baz>
    Keys extends keyof Props ? { [K in Keys]: Props[K] } : never;
// Keysがkeyof Propsを継承しているなら{foo: number} | {bar: string} | {baz: boolean}という型を返し、継承していないなら、neverを返しますということです。
// Keys = foo | bar | bazで、keyof Props = foo | bar | bazなのでKeys = keyof Props 
// つまりKeysがkeyof Propsを継承しているなら、foo | bar | bazのどれかの型になります。
// 継承されないというのは、コードが正しい限りありえないのでNeverになります。


// RequireEitherの型変数Propsに代入される型
type Hoge = RequireEither<{
    foo: number;  bar: string;  baz: boolean;
}>;
```

本当に、foo | bar | bazのどれかの型になるのか気になりました。

## 解答例のif文と返り値

```typescript
function func(hoge: Hoge) {
    let n: number;
    let s: string;
    let b: boolean;
    const { foo = 0, bar = '', baz = false } = hoge;
    if ('foo' in hoge) {
        // hogeの引数のなかに'foo'がある時 
        // {foo:number}と断定される
        n = hoge.foo;  s = hoge.bar;  b = hoge.baz;
    }
    if ('bar' in hoge) {
        // hogeの引数のなかに'bar'がある時 
        // {bar:string}と断定される
        n = hoge.foo;  s = hoge.bar;  b = hoge.baz;
    }
    if ('baz' in hoge) {
    // hogeの引数のなかに'baz'がある時 
    // {baz:boolean}と断定される
        n = hoge.foo;  s = hoge.bar;  b = hoge.baz;
    }
    if ('foo' in hoge && 'bar' in hoge) {
    // hogeの引数のなかに'foo'と'bar'がある時 
    // {foo:number, bar:string}と断定されるが
    // {foo:number}|{bar:string}|{baz:boolean}のいずれの選択肢にも存在しないので
    // hoge は never となる。never からのプロパティアクセスはすべてエラー。
        n = hoge.foo;  s = hoge.bar;  b = hoge.baz;
    }
    if ('foo' in hoge || 'bar' in hoge) {
        // hogeの引数のなかに'foo'または'bar'がある時　
        // {foo:number}|{bar:string}の2種類に絞り込まれるが、
        // 1. hoge.foo は {bar:string} に存在しておらず絞り込みが不十分なのでエラー
        // 2. hoge.bar は {foo:number} に存在しておらず絞り込みが不十分なのでエラー
        // 3. hoge.baz はいずれにも存在しないのでエラー
        n = hoge.foo;  s = hoge.bar;  b = hoge.baz;
    }
}

func({});// foo | bar | bazのどれでもないのでエラー
func({ foo: 42 }); // 'foo' in hogeの場合
func({ bar: 'some string' }); // 'bar' in hoge
func({ baz: true }); // 'baz' in hoge
func({ hogehoge: true }); // foo | bar | bazのどれでもないのでエラー
```

ということはこれらを呼び出すとこのようにエラーになると思いました。

```typescript
// {foo:number, bar:string}は{foo:number}|{bar:string}|{baz:boolean}のいずれの選択肢にも存在しないのでneverとなり
// if ('foo' in hoge && 'bar' in hoge)このif文条件から外れ
// if ('foo' in hoge || 'bar' in hoge)このif文条件からも外れエラーになる
func({ foo: 42, bar: "string" });
// 通る条件がないのでエラーになる
func({ foo: 42, baz: true });
// 通る条件がないのでエラーになる
func({ bar: "star", baz: true });
// {foo:number, bar:string, baz: noolean}は{foo:number}|{bar:string}|{baz:boolean}のいずれの選択肢にも存在しないのでneverとなり
// if ('foo' in hoge && 'bar' in hoge)このif文条件から外れ
// if ('foo' in hoge || 'bar' in hoge)このif文条件からも外れエラーになる
func({ foo: 42, bar: "star", baz: true });
```

気になる実行結果がこちらです。
![スクリーンショット 2020-02-08 12.22.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384722/e4745ef2-1626-3697-4ccf-7d192122f7ab.png)

なんと、エラーになりませんでした。

# なぜエラーにならないのか？
めっちゃ不思議でした。
いろんな人に聞いた結果一番納得のいった答えがこれでした。

> 第一Generics は何も制約がかかっていないので、何でも含むことができます。
利用時 either が働かないのはこのため。
> [Twitter|@takepepe](https://twitter.com/takepepe/status/1225227228889276416?s=20)

つまり第1ジェネリクスである`Props`に型を１つしか受け取らない制約がないからです。
その制約はつまり、 `Intersection Types`で受け取った`RequireEither`を`Union Types`でに変換し絞り込むということです。

その方法がないか一緒に考えてくれた方とめっちゃ調べました。
[このページ](https://gitter.im/Microsoft/TypeScript?at=5dd8bfeed2c92c2275ddd400)でそのことについて言及されていたので載せます。
![スクリーンショット 2020-02-08 12.57.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384722/fc0a5fe5-7d7b-15f8-be47-42d266650582.png)


> I've seen a lot of unionToIntersection but can you the reverse?
> { name: string } & { age: number } to { name: string } | { age: number }?

`Intersection Types`から`Union Types`に変換することは可能ですか？と聞いてます。

それに対しての返答です。

> You can't. It only works in one direction because TS distributes over unions, but not over intersections

You can't.なのでできません。
TSでは`Union Types`を介しての分割できるが、`Intersection Types`を介しての分割はできない。という回答です。

一緒に考えてくれた方が、問題作成者の方に
* エラーを期待したがエラーにならなかったこと
* TSでは`Union Types`を介しての分割できるが、`Intersection Types`を介しての分割はできない
こと

お伝えしてくれました。その時の返信がこちらです。
> ご指摘の通り、foo, bar両方のキーを持つオブジェクトをfuncの引数に渡してもエラーになりません。
“either”とするには都合が悪いため、問題のコードのfunc使用箇所ではあえてその性質を伏せています。

つまり、完全な`Either`は実現できないということです。
画像の性質は完全なる`Either`ではなかったということもわかりました。

# 参考にさせていただいた記事
* ジェネリクス
  * [TypeScriptのジェネリックについて調べてみた](https://qiita.com/rindarinda5/items/9b65b637257f67b93141)
  * [TypeScriptの型入門#ジェネリクス](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a#%E3%82%B8%E3%82%A7%E3%83%8D%E3%83%AA%E3%82%AF%E3%82%B9)
  * [TypeScript#Generics](https://www.typescriptlang.org/docs/handbook/generics.html)
  
* keyof
  * [TypeScript 2.1のkeyofとかMapped typesがアツい#keyof](https://qiita.com/Quramy/items/e27a7756170d06bef22a#keyof)
  * [TypeScript2.1.4で導入されたkeyofキーワードとinキーワード、そしてLookup TypesとMapped Types](https://blog.yux3.net/entry/2017/02/08/033834)
  
* UnionTypes
  * [TypeScript Deep Dive日本語版#Union型](https://typescript-jp.gitbook.io/deep-dive/type-system#yunionunion-type)
  * [TypeScriptの型入門#Union型](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a#union%E5%9E%8B%E5%90%88%E4%BD%B5%E5%9E%8B)
  * [TypeScript#UnionTypes](https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types)

* ConditionalTypes
  * [TypeScriptの型入門#ConditionalTypes](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a#conditional-types)
  * [TypeScript#ConditionalTypes](https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types)
* MappedTypes
  * [TypeScript2.1.4で導入されたkeyofキーワードとinキーワード、そしてLookup TypesとMapped Types](https://blog.yux3.net/entry/2017/02/08/033834)
  * [TypeScript 2.1のkeyofとかMapped typesがアツい#MappedTypes] (https://qiita.com/Quramy/items/e27a7756170d06bef22a#mapped-types)
  * [TypeScriptの型入門#MappedTypes](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a#mapped-types)
  * [TypeScript#MappedTypes](https://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types)

  
* in
  * [TypeScript2.1.4で導入されたkeyofキーワードとinキーワード、そしてLookup TypesとMapped Types](https://blog.yux3.net/entry/2017/02/08/033834)

* UnionDistribution
  * [TypeScruptの型初級#conditional typeにおけるunion distribution](https://qiita.com/uhyo/items/da21e2b3c10c8a03952f#conditional-type%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8Bunion-distribution)
  * [TypeScript: 共用体型(union)を交差型(intersection)に型変形する方法#comment](https://qiita.com/suin/items/93eb9c328ee404fdfabc#comment-5218b3e9d13d93dfc98f)
  
* LookupTypes
  * [TypeScript 2.1のkeyofとかMapped typesがアツい#T[K]](https://qiita.com/Quramy/items/e27a7756170d06bef22a#tk) 
  * [TypeScruptの型入門#LookupTypes](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a#lookup-types-tk)
  
* IntersectionTypes
  * [TypeScruptの型入門#IntersectionTypes](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a#intersection%E5%9E%8B%E4%BA%A4%E5%B7%AE%E5%9E%8B)
  * [TypeScript#IntersectionTypes](https://www.typescriptlang.org/docs/handbook/advanced-types.html#intersection-types)

**SpcialThanks**:[りょー](https://twitter.com/ryo4429399
),[mpyw](https://twitter.com/mpyw),[Takepepe](https://twitter.com/takepepe)

この記事書いてて頭が何度もおかしくなりましたが、色々なことが学べて楽しかったです！
多分、とても至らない部分あると思うので編集依頼などで教えていただければ幸いですヽ(；▽；)ノ
ここまで、読んでいただきありがとうございました。

[この解答例の最終的解説付き](https://www.typescriptlang.org/play/index.html#code/C4TwDgpgBAShCOBXAlgJwgUWcAFhVAPAAqoD2YAzgDRQDSEIFUEAHsBAHYAmTA1g6QBmUEuSYBeKPxBCRZSgD4o4gFAB6NbAQp0WXPgKDSpKADIoAIwCGqM5asAvGkZMAfe7ffWHClVH90DEys7Nx8AsKilFAA-FAA3lAA2rRQyByBjAC6AFxyYilZUAC+UHkcEABu+ADc6pr0jIAyDNKyURSASQyAmb6A-kaA6gyAZgyAIgyA0QyAVgyAkQzxLuWIALYW+KXu8daoeRTAqOkA5ssJ3nkWxgA2EFYcxYAWDEOAYgyA0eodgCvxfYCADL2DQ2NfE68V1ahnn1AH4MgE0GG63QDKDFdAOYMoMAQAz1TISKAuKBeGzo+wOGGvVqReQotEYzzYsaAOwYYY0Ufj8tEkYATBmBgCiGaktCJ0zofYbjX7EjxY7zkwCWDIAYhkA0gzk+6AawYxsywYiNFBeoBVBlF3wh5MA9gyvQDNDIAfhkAkwxNQDG1n0hoAFNOZgCEGZmACQZvpSAHJVfCy+UIlRIuBINCYbB4VBSwCQmoAHU3a0sAx3KAU0U1SN7ipQJAoAAJUg7aCSH06f36QjxPwBGZQDjzRaoGr+NYbLa7SvYo6nc4cOrFBR1FSCRAcADGwGQpAyXd7AAocOmIHk0xmAJQJQv+M7AEuzBa1BdQJdQCg17YcHZ1AKbiDLiyN0hnC6HgI9webBKo4zKKAABhoa2fAHJP+-HM-BFYJwUNApSSOOGbXv4yDCCOn4uJ+aQZOBEBzgWR5HkqyHkmM4rSnBxifk01ojIAQmZQBuGGaIkxalmutjXIAtaaAFnacYUQEGRgROAB0Lj1iiyFcWs9YWM+AneJBJQbtBUCwWsCHpFAyGoWx-iYRO2G4Z+clEaR5HoQESqJNW261vuJRXMxrH6f4HGKdxvH+Px3FCVWonOY4EnFFJMFaY48lIROymUXZGYaXh3iEcRZEboZhyWE2FyMSxoojCpJZuRmPHGHxGUQIJNjCbl+UOJ53kyfhpD+SF0CmOYvmoFVSnzsFWE4Xh8FXPVkW6TFVEzLR5a-usmx7nsFnJSMTS9Qk-VlksrirDYOQjbsxQLYcxwXs2xTkkMgBaDKK5KAB4JgB5RoARQHSoAQQyABragAU6n0jowtNyFQFqJaurYVxjCM8L-PgUDihM5KAOsMgC3DIAiwyAGMMgDFDIARQyAPUMgDdDIAnQxaqCgCeDAMgAVDIAlwz6vCiLWelnGZQ525FS5lgUx5G5eUe0mwfBiHVei7hdczTVoQZmitZp8HAoA+gxal1OkkYAAAzTdMxg5AN82LcNpl7OSABMgB0XoAehnSoAer7MoAPfHAilTSvNNACMXHVVlJhvfLy2K6U0p3YMgBSDBMe067rgD+DE0gCwcoAgsqAGBKFIwrj+rTcr5tiZi1uzXR9uOwMLtu3rXt+4HlIh9NADMEfuQ4r37aKV2O49GdE7ZAlk05mWUyJJN5eJtMqF5nbdj2I7xMUM41Eq-IfhiDgiqKMKXSXeMt6O1HS1AAAsyslF3UBKhVjXqYALBqABAq49t0ZS1QJ+FCkHM0ArfuCGd5WS9yRzE5b+3DZQFsiAgQvl9+dfGa34kyHIXkj-Pxfmhe6Yn7oPYeo9Q5IilqQGWc1UBDVtqNYoWooEwNjutJaJ89joIcDkTal5Li7QOsdc6Rd7qPT+qgL6zIkQM2Xu-GqdUr4KSUpCck0FADhpoAQ3NABvcoDQAaJqihoT5JmzCJysz3kwgKs5WEcJ4YDS6AiQ6ylSsObej5oEz2VvAqAAAiTBOj551CVIALASRg8KaOA5Rn91F5Fnr+HBD9UBP0MUiUx5jLHfWscZPRwAbA6Psb-Jx-9IEx0Gh4BBuwAklgSpcZBoS5bVkwWtRaOC8HbUIYdU6F0bpkKGJSChVChHlREVIhhEibAr2kWwwQXDeETAEUUxmBF6HiPZqIqpsi6kKNFEozxqi77FjseE3Rmw-FRL-i4oAA)
[Takepepeさんによる別途回答(複数の型をerrorにしてくれています)](https://www.typescriptlang.org/play/index.html?ssl=2&ssc=67&pln=2&pc=72#code/PTAEFUDsEsHtNAFQJ4AcCmpBJDKAkpAF3QCcBndAYwLgRQ1EGsGAKALU3ACZcAeRAPlABeUAApEodAA8ikACalQAQ0jIA-KIDWALiQBKIQIBusaLNA7I6QyX1SZ8zTuiQAZiTz7BRk2fW5zoJbWxEwgoADeAL7YgIxRgJ3agOoMgFYMgCIMLGygAHKwBABCADbKGrwCwuJ26HIK4aAA2gDSoM6gGujIsC5IALoWVu7R6gDkgLwbgOB7oIBeXoDGDIC+DIDmDICaDIARDIMBiKFgAIyAJgyAdgyA6wyAtwyAiwyAYwyAxQwJgNIMgIDGU4BmDKmAbEqACmnp9ADylgAKxLCoJSEEC43Fa7U6-Ak0kqDiC7iGY1AgFV5QDQCoAghhu90AfgyAbQZAMkMqx02TyhUgxX4Gwi0V2gFo5QDVcVhABYMgCiGQAGDAtABD-gAA5QB1boBTJUA8gyASH-3uxIAAjWCSQFlKH2ap1RrNMEdbpOVzucCgAYQAJwkJhMGAWBUGC02kasHdAPoMoA+4oAVpQCNhAA5RgECGQA98YAAhlFoFyUplEUVTQQKohPVAACVKLBiLJuPUADRIBpdASRSn+yTYQBgSoBjyIS9wgMHgSAyDEAkJqAbeMFoBohl9WagNEDUEl0qzJT4lIAMrBYBoAK6oDmAU0VANlKgBIFQCDDIByhkAwwyAeoYhYAZBkAo-qAQMiGEla1a3YBABkARamAB1NANHqvujAEcB9BiOgAKLQAgACxIge43z4IifsAA5ugdI2JaQICABkto-H8AIfp4AjKMglLHieWDbAK9asPQAASv6YMIl7XreD7Pq+NQuH2FgDgAtuKJAANygOKijEDopAEMQzg-nRDEAF46JKsD5Ogyjal2TAUPALGgC4A6QBQOhYX+QLfgpXgREwoCgAJzqQORVEkGpGnoM6pDMax7H6Zp9G8X2AnKPpYmQBJJF9kCAAMyYMcQQKDIM7mKFxQIuIo+TkNqinYTR+nQJ0IiDKRsCrM0SnoPo4T6epCDCElAB0cVpaACiZdhWUeXl4phX+xV+fpGbqVFoiDB5CUIElKV5RloDZbl6nqQVHVFSV3X0eV6CVVx1WRdFDV+U1fV-q1g3tZ1fZ5b12UDd1ZWFRV3HjbVk1xTNSWgCBYFTcQh3YfN3WLUVcWgGEiAfAAIh8gBY-6AgBMaYASgyPGkg2rf1jGlcNo27U0+19hdCkAD7Q6AZ1Q8lqkLSDXXdQD21A4Nm2zSNO3qRmGZMFJMkiDUkS6MT0kUGTklkaAAAsHDapTJM0zUHk6IMpCwBRmAsWxkA-qsFNU6THN+TorEDpgots7TSVJVLxAyyzYvs3TsA6EzvlMaAABEAvsfrLMRfLTla4zHC+TxoDS7Luhm9TtOcwbLGMfrNvK6rFNO+Lmva9b9GMTohsEB7Xt2yrDs0UAA)

(修正５回ほどしました)