TypeScriptの型演習（解答・解説編）

この記事は、「TypeScriptの型演習」の解答および解説を収録した記事です。問題に挑戦したい方は先に下記の記事をご覧ください。

- [TypeScriptの型演習](https://qiita.com/uhyo/items/e4f54ef3b87afdd65546)

## 1-1. 関数に型をつけよう

```ts
function isPositive(num: number): boolean {
    return num >= 0;
}
```

関数の引数に`: number`を追加しました。これが引数に対する型アノテーションです。TypeScriptでは、関数の引数の型を指定するにはこのように明示するのが基本です。型が指定されていない場合はエラーとなります（`--noImplicitAny`オプションの効果）。ただし、問題1-3のように文脈から推論できる場合は引数の型アノテーションを省略できます。

また、関数にはこのように返り値の型アノテーション（`: boolean`）も可能ですが、返り値については関数の定義から推論してくれます。今回の場合、返り値が`num >= 0`であり、これの型が`boolean`であることを推論してくれます。

型アノテーションが何だか分からなかった方は、この機会に理解しておきましょう。

### 別解
```ts
function isPositive(num: number) {
    return num >= 0;
}
```

前述のようにTypeScriptは返り値の型を推論できますから、これくらい簡単な関数ならば敢えて`: boolean`と書かなくてもよいかもしれません。

## 1-2. オブジェクトの型

```ts
interface User {
    name: string;
    age: number;
    private: boolean;
}
```

オブジェクトの型は、このように`interface`構文を使って定義するのがよく見られます。`プロパティ名: 型;`という形でプロパティを列挙しましょう。

### 別解
```ts
type User = {
    name: string;
    age: number;
    private: boolean;
};
```

このように`type`文を用いて型を定義してもよいです。オブジェクト型は`{ name: string; age: number; private: boolean; }`のように書くことができ、これに`User`という名前を付けていると見なせます。

## 1-3. 関数の型
```ts
type IsPositiveFunc = (arg: number) => boolean;
```

関数の型はこのように`(引数名: 型) => 返り値の型`という形で書くことができます。型システム上、型にかかれている引数名（`arg`）に意味はありません。

問題とは関係ありませんが、`isPositive`に代入されている関数`num => num >= 0`に引数の型アノテーションが無いという点は注目に値します。この場合、関数の代入先である`isPositive`の型が`IsPositiveFunc`であることから、引数`num`の型が`number`であると推論できます。これが、1-1の解説で触れた「引数の型が文脈から推論できる場合」に相当します。

### 別解
```ts
interface IsPositiveFunc {
  (arg: number): boolean;
}
```

このように、オブジェクト型の記法でも関数の型を定義することもできます。

## 1-4. 配列の型
```ts
function sumOfPos(arr: number[]): number {
  return arr.filter(num => num >= 0).reduce((acc, num) => acc + num, 0);
}
```

`number`型の値の配列の型は`number[]`と書くことができます。

### 別解
```ts
function sumOfPos(arr: Array<number>): number {
  return arr.filter(num => num >= 0).reduce((acc, num) => acc + num, 0);
```

配列の型は`Array<number>`のように書くことも可能です。

## 2-1. ジェネリクス
```ts
function myFilter<T>(arr: T[], predicate: (elm: T) => boolean): T[] {
  const result = [];
  for (const elm of arr) {
    if (predicate(elm)) {
      result.push(elm);
    }
  }
  return result;
}
```

`myFilter`に型引数`T`を追加しました。型`T`は渡される配列の要素の型を想定しています。そのため、第1引数の型は`T[]`となります。第2引数は配列の要素を1つ受け取って真偽値を返す関数なので、型は`(elm: T) => boolean`となります。

`myFilter`の返り値は`T[]`です。実は返り値の型アノテーションを省略してもちゃんと推論してくれるので、それでもOKです。なかなか賢いですね。

## 2-2. いくつかの文字列を受け取れる関数
```ts
type Speed = "slow" | "medium" | "fast";
```

`"slow"`などは文字列のリテラル型であり、`"slow"`という文字列のみを受け付ける型です。また、`|`はユニオン型の記法であり、複数の型のどれか1つに当てはまる型という意味になります。よって、ここで定義した型`Speed`は`"slow"`という文字列または`"medium"`という文字列または`"fast"`という文字列の型となり、問題の要件を満たしています。

`"slow"`や`"medium"`といった文字列は`Speed`型を持つので`getSpeed`関数の引数に渡すことができますが、`"varyfast"`のような他の文字列は`Speed`型を持たないため型エラーとなります。

また、この例では`getSpeed`関数の中身も注目に値します。中身のコードを読むと明らかに、この関数が数値を返すのは引数が`"slow"`か`"medium"`か`"fast"`のときだけです。万が一他の値が渡された場合には、この関数の末尾に到達して何も返りません（`undefined`が返ることになります）。しかし、TypeScriptは引数が`Speed`型であるという情報を用いて「この関数は必ずどれかの`return`文に到達する」と推論します。それによって、関数の返り値型を`number`とすることが許されています。試しに`Speed`型に`"veryfast"`のような他の文字列を追加するとエラーになることが分かります。

## 2-3 省略可能なプロパティ
```ts
interface AddEventListenerOptionsObject {
  capture?: boolean;
  once?: boolean;
  passive?: boolean;
}
declare function addEventListener(
  type: string,
  handler: () => void,
  options?: boolean | AddEventListenerOptionsObject
): void;
```

この問題の最初のポイントは、`AddEventListenerOptionsObject`型の定義です。各プロパティに`?`がついており、これはそのプロパティが省略可能であることを表しています。そのため、`{}`や`{capture: false, once: true}`などのオブジェクトは`AddEventListenerOptionsObject`型を持ちます。（なお、言うまでもありませんがわざわざ`AddEventListenerOptionsObject`型を定義したのは分かりやすさのためです。この型に名前を付けずに`addEventListener`の定義中で使うことも可能です。）

また、第3引数はこのオブジェクトの他に真偽値の引数も受け付けることになっています。したがって、第3引数の型は先述のユニオン型を用いて`boolean | AddEventListenerOptionsObject`になっています。これが2つ目のポイントです。

最後に、第3引数は省略可能でした。このことを表現するために、`addEventListener`の宣言で`options`引数に`?`が付いています。なお、`addEventListener`の型は`(type: string, handler: () => void, options?: boolean | AddEventListenerOptionsObject) => void`となり、型にもこの`?`が残っていることが分かります。

## 2-4. プロパティを増やす関数
```ts
function giveId<T>(obj: T): T & { id: string } {
  const id = "本当はランダムがいいけどここではただの文字列";
  return {
    ...obj,
    id
  };
}
```

この問題のポイントは返り値の`T & { id: string }`型です。これはインターセクション型であり、オブジェクトに新しいプロパティを増やしたい場合の典型的な方法です。`T`がオブジェクトの場合、これは`T`のプロパティと`id`プロパティを両方持つようなオブジェクトの型になります。

ただし、TypeScriptは賢いので実は返り値の型アノテーションを書かなくても勝手に推論してくれます。それだとこの問題の意味がないのでやめてほしいですが。

## 2-5. useState
```ts
type UseStateUpdateArgument<T> = T | ((oldValue: T) => T);
declare function useState<T>(
  initialValue: T
): [T, (updator: UseStateUpdateArgument<T>) => void];
```

この問題の新しい点はタプル型の使用です。タプル型は要素ごとに型が異なる配列を表す型でしたので、`useState`のようなAPIに型を付けるのに適しています。

`UseStateUpdateArgument<T>`はステート更新関数の引数の型です。ステート更新関数は、新しいステートの値を直接受け取るか、新しいステートを古いステートから計算する関数を受け取るかの両方が可能です。これを表すためにユニオン型を使用しています。

## 3-1. 配列からMapを作る
```ts
function mapFromArray<T, K extends keyof T>(arr: T[], key: K): Map<T[K], T> {
  const result = new Map();
  for (const obj of arr) {
    result.set(obj[key], obj);
  }
  return result;
}
```

今回、`mapFromArray`は2つの型引数を持ちます。1つ目は`T`で、これは渡される配列の要素の型です。2つ目は`K`で、2つ目の引数の型です。これは使用するプロパティ名を表すリテラル型を期待しています。引数`key`で指定されるプロパティ名は`T`が持つプロパティの名前でなければいけませんから、型引数の制約にそのことを書いています。それが`K extends keyof T`の部分です。これは`K`が`keyof T`の部分型でなければいけないということを示しており、`keyof T`は`T`が持つプロパティ名いずれかの型です。今回の使用例では、`T`は`{id: number; name: string}`なので`keyof T`は`"id" | "name"`となっています。`K`はその部分型（つまり`"id" | "name"`に当てはめることができる型）なので、`"id"`や`"name"`、`"id" | "name"`などが可能です。問題の使用例では`mapFromArray(data, "id")`として使用されていますから、`K`には`"id"`という型が入ります。

返り値の`Map`型は2つの型引数をとる型です。1つ目はキーの、2つ目は値の型です。今回、Mapのキーとなるのは各オブジェクト`obj`の、`key`で指定されたプロパティ、すなわち`obj[key]`の型です。例えば`key`が`"id"`の場合は`obj["id"]`の型となります。いまオブジェクトの型は`T`で、キーの名前はリテラル型として`K`に入っていますから、プロパティアクセス型を用いて`obj[key]`の型は`T[K]`と表現できます。

Mapに入る値はオブジェクトそのものなので、2つ目の型引数は普通に`T`です。

以上が解答の説明です。なお、今回は返り値の型のアノテーション（`Map<T[K], T>`）を省略してしまうとTypeScriptが推論できず、`Map<any, any>`にされてしまいます。このように型アノテーションで指示する方法の他に、`new Map()`を`new Map<T[K], T>()`として型を教えてあげる方法もあります。

## 3-2. Partial
```ts
type MyPartial<T> = { [K in keyof T]?: T[K] };
```

Mapped typesの基本的な利用法です。`MyPartial<T>`は、`keyof T`に属する各プロパティ名`K`に対して、型`T[K]`を持つ`K`という省略可能なプロパティが存在するようなオブジェクトの型となります。`T[K]`というのは元のオブジェクトのプロパティの型と同じですから、結果として`MyPartial<T>`は元々のオブジェクトの各プロパティが省略可能となっただけのオブジェクトの型となります。

## 3-3. イベント
```ts
class EventDischarger<E> {
  emit<Ev extends keyof E>(eventName: Ev, payload: E[Ev]) {
    // 省略
  }
}
```

問題文がややこしい割に、やることは単純です。今回のように、引数に渡された文字列に応じて型の挙動を変えたい場合はその文字列をリテラル型として取得するのが定番です。これは3-1でもやりましたね。今回は型引数`Ev`を第1引数の型としました。例えば`ed.emit("start", { ... })`の場合、`Ev`には`"start"`型が入ります。さらに、`Ev extends keyof E`とすることによって、`E`に定義されていないイベント名を拒否しています。`ed.emit("foobar", { ... })`のような呼び出しはこれによって型エラーとなります。

イベント名が型`Ev`として得られているので、第2引数の型は`E`から適切なものを取得します。`E`が`イベント名: データの型`という形のオブジェクトなので、目的の型は`E[Ev]`で得られます。

## 3-4. reducer
```ts
type Action =
  | {
      type: "increment";
      amount: number;
    }
  | {
      type: "decrement";
      amount: number;
    }
  | {
      type: "reset";
      value: number;
    };

const reducer = (state: number, action: Action) => {
  switch (action.type) {
    case "increment":
      return state + action.amount;
    case "decrement":
      return state - action.amount;
    case "reset":
      return action.value;
  }
};
```

アクションの型を`Action`とし、ユニオン型を用いて定義しています。いわゆる代数的データ型を模したパターンであり、TypeScriptプログラミングでは頻出です。

## 3-5. undefinedな引数
```ts
type Func<A, R> = undefined extends A ? (arg?: A) => R : (arg: A) => R;
```

`A`が`undefined`かどうかで動作を変えたいということで、conditional typeの出番です。`undefined extends A`は正確には`undefined`型が`A`型の部分型であるという条件を表しています。簡単な言葉で言うとこれは`A`が`undefined`を受け入れる型であるかどうかを判定しています。例えば`A`が`undefined`である場合のほかに、`A`が`number | undefined`の場合も合致します。`A`に`undefined`を指定可能な場合は引数を省略可能にしたいという問題の趣旨に合致していますね。

`undefined`が`A`型の部分型であるとき`Func<A, R>`は`(arg?: A) => R`となります。これにより、引数の省略がOKになります。そうでないときはこれまで通り`(arg: A) => R`です。

## 4-1. 無い場合はunknown
```ts
function getFoo<T extends object>(
  obj: T
): T extends { foo: infer E } ? E : unknown {
  return (obj as any).foo;
}
```

この問題は、conditional typeにおける`infer`の典型的な使用例です。まず、`getFoo`に型引数`T`を持たせることで、引数`obj`の型を`T`として取得しています。この際`T extends object`という制約を与えることで、オブジェクトでないものが渡されるのは型エラーとしています。

返り値は`T extends { foo: infer E } ? E : unknown`であり、ここでconditional typeが使われています。`T extends { foo: infer E }`というのは、`T`が`foo`プロパティを持つ型かどうかで場合分けをするという意味です。また、持つ場合は、`foo`プロパティの型を`E`として取得します。条件が満たされる場合の返り値の型は`E`、すなわち`foo`プロパティの型です。条件が満たされない場合の型は、問題文に従い`unknown`としています。

なお、conditional typeが関わっている場合は例によってTypeScriptの型推論能力は頼りになりません。この場合はそのままだと`obj.foo`は存在しないというエラーが出るので、`(obj as any)`とすることでエラーを抑制する必要があります。

## 4-2. プロパティを上書きする関数
```ts
function giveId<T>(obj: T): Pick<T, Exclude<keyof T, "id">> & { id: string } {
  const id = "本当はランダムがいいけどここではただの文字列";
  return {
    ...obj,
    id
  };
}
```

`giveId`の引数`obj`の型を型引数`T`とするのはいつも通りです。返り値の型は`Pick<T, Exclude<keyof T, "id">> & {id: string}`です。

`Pick`と`Exclude`はTypeScriptの標準ライブラリで定義されている型です。`Pick<T, K>`はオブジェクト`T`のうち名前が`K`に含まれるプロパティのみを持つようなオブジェクトの型を返します。例えば`Pick<{foo: number; bar: string}, 'foo'>`は`{foo: number}`となります。`K`部分に`"foo" | "bar"`のようなユニオン型を与えることで複数プロパティを持ったオブジェクト型を得ることができます。

`Exclude<T, U>`は`T`がユニオン型のとき、`T`の構成要素のうち`U`の部分型であるものを除いた型になります。今回の場合、`keyof T`は`T`のプロパティ名全てのユニオン型ですから、`Exclude<keyof T, "id">`は`T`の`"id"`以外のプロパティ名全てのユニオン型となります。なお、`keyof T`に`"id"`が含まれない場合は`Exclude<keyof T, "id">`は`keyof T`です。

これらを組み合わせることで、`Pick<T, Exclude<keyof T, "id">>`は`T`から`id`プロパティを除いたオブジェクトの型となります。まずこの型を作ることで`id`プロパティを消し、それから`{ id: string}`とのインターセクション型を付けることで`id`プロパティの上書きを達成しています。

なお、この`Pick<T, Exclude<keyof T, K>>`というパターンは頻出なので`Omit`という名前が付けられているのをよく見ます。

```ts
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
```

### 別解
```ts
function giveId<T>(
  obj: T
): { [K in keyof T]: K extends "id" ? string : T[K] } & { id: string } {
  const id = "本当はランダムがいいけどここではただの文字列";
  return {
    ...obj,
    id
  } as any;
}
```

`Pick<T, Exclude<keyof T, "id">>`の代わりにconditional typeを使って`T`の`id`プロパティの型を`string`に書き換えたものを作る解法です。`T`に`id`プロパティが存在しなかった場合のために`& {id: string}`はやはり必要です。

## 4-3. unionは嫌だ
```ts
type Spread<Ev, EvOrig, E> = Ev extends keyof E
  ? EvOrig[] extends Ev[]
    ? E[Ev]
    : never
  : never;
class EventDischarger<E> {
  emit<Ev extends keyof E>(eventName: Ev, payload: Spread<Ev, Ev, E>) {
    // 省略
  }
}
```

Conditional typesの性質をよく理解している必要がありやや難しい問題です。この問題では、型引数`Ev`が`"start" | "end"`のようなユニオン型なのか、それとも`"start"`のような単一のリテラル型なのかを判断する必要があります。そのために、`payload`の型である`Spread<Ev, EvOrig, E>`ではまず`Ev extends keyof E`という条件分岐によりunion distributionを発生させ、`Ev`をユニオンの構成要素に分解します。なお、`Ev extends keyof E`は常に条件を満たしますので、union distributionを発生させる以外の意味はありません。else側に特に意味はないので`never`としています。

次の`EvOrig[] extends Ev[]`という条件判定は、`EvOrig`が`Ev`の部分型かどうか判定する目的で行っています。`[]`として配列型にしているのは`EvOrig`に対してunion distributionが発生するのを防ぐためです。`EvOrig`は元々の（union distributionが起きる前の）`Ev`です。

もし元々の`Ev`が`"start"`のような単一のリテラル型だった場合、ここで`Ev`と`EvOrig`は両方`"start"`となりますから条件を満たします。その結果`E[Ev]`が得られます。一方、元々の`Ev`が`"start" | "end"`のようなユニオン型だった場合、`Ev`は`"start"`で`EvOrig`は`"start" | "end"`という状況になります。この場合は`EvOrig[] extends Ev[]`は満たせませんから型は`never`となります。`Ev`に対してunion distributionが発生しているとはいえ、どの`Ev`に対しても結果は`never`なので、結果として`Spread<Ev, EvOrig, E>`は`never | never | ... | never`となり、それは`never`と等しくなります。

結局、`Spread<Ev, Ev, E>`は、`Ev`が単一のリテラル型のときは`E[Ev]`となり、そうでないときは`never`となります。`never`型はどんなオブジェクトも持ち得ない型ですから、（TypeScriptの型システムを欺いて`never`型の値を作ったりしない限りは）このとき引数`payload`に何を渡しても型エラーとなり、目的を達成することができました。

## 4-4. 一部だけPartial
```ts
type PartiallyPartial<T, K extends keyof T> = Partial<Pick<T, K>> &
  Pick<T, Exclude<keyof T, K>>;
```

4-2の類題です。`T`を`K`に属するプロパティを持つ部分とそうでない部分に`Pick`を使って分け、前者に`Partial`を適用したあと再び結合しています。

## 4-5. 最低一つは必要なオプションオブジェクト
```ts
type PartiallyPartial<T, K extends keyof T> = Partial<Pick<T, K>> &
  Pick<T, Exclude<keyof T, K>>;

type AtLeastOne<T> = Spread<T, keyof T>;
type Spread<T, K extends keyof T> = K extends keyof T
  ? PartiallyPartial<T, Exclude<keyof T, K>>
  : never;
```

筆者の以前の記事「[TypeScriptで最低一つは必須なオプションオブジェクトの型を作る](https://qiita.com/uhyo/items/583ddf7af3b489d5e8e9)」を読んでいる皆さんにとっては簡単なボーナス問題です。ひとつ上の`PartiallyPartial<T, K>`を再利用しています。

詳しくはあの記事を読んでいただきたいですが、目標はもともとの`Options`に対して

```ts
PartiallyPartial<Options, 'bar' | 'baz'> | PartiallyPartial<Options, 'foo' | 'baz'> | PartiallyPartial<Options, 'foo' | 'bar'>
```

という型を生成することです。そのために、`keyof T`で得られる`'foo' | 'bar' | 'baz'`のそれぞれの要素をunion distributionを用いながら`PartiallyPartial<...>`に変換します。

## 4-6. ページを描画する関数
```ts
type PageGenerators = {
  [P in Page["page"]]: (page: Extract<Page, { page: P }>) => string
};
```

今回、ページの種類は`"top"`、`"mypage"`、`"ranking"`の3種類です。これらのユニオン型は`Page["page"]`として取得できます。`PageGenerators`型のオブジェクトはこれらの名前のプロパティを持っていなければいけませんから、`[P in Page["page"]]`というmapped typeを利用します。右辺の型で`P`はそれぞれ`"top"`, `"mypage"`, `"ranking"`というリテラル型になります。

右辺の型は関数となる必要がありますが、例えば`P`が`"mypage"`の場合は`{ page: "mypage", userName: string }`という型が引数に渡される必要があります。この型を得るのが`Extract<Page, { page: P }>`という部分です。`Extract`は標準ライブラリにある型で、ユニオン型である`Page`に属する要素のうち`{ page: P }`の部分型であるものだけが抜き出されます。これにより、`P`が`"mypage"`の場合は、`Page`の中から`{ page: "mypage", userName: string }`だけが残ります。

## 4-7. 条件を満たすキーだけを抜き出す

```ts
type KeysOfType<Obj, Val> = {
  [K in keyof Obj]-?: Obj[K] extends Val ? K : never
}[keyof Obj];
```

これは、TypeScriptでキーの型を扱いたいときにmapped typeを経由するという、結構頻出のテクニックです。これは、まずmapped type `{ ... }`でオブジェクト型を作り、それに対してLookup型`[keyof Obj]`でアクセスしています。

前半部分のmapped typeを抜き出してみましょう。

```ts
type Data = {
  foo: string;
  bar: number;
  baz: boolean;

  hoge: string;
  piyo: number;
};

type Mapped<Obj, Val> = {
  [K in keyof Obj]-?: Obj[K] extends Val ? K : never
};

/* T1は
 {
   foo: "foo";
   bar: never;
   baz: never;
   hoge: "hoge";
   piyo: never;
 }
 型
*/
type T1 = Mapped<Data, string>;
```

このように、mapped typeを用いると、`Obj`と同じキー名を持ちつつ、各キーの型が「条件を満たすならキー名そのもの、条件を満たさないなら`never`」となるようなオブジェクト型を作ることができます。ここで条件を満たさない場合に`never`を用いたのがポイントです。この`never`型は、ユニオン型を取ると消えてしまいます。このmapped typeをよく見ると`-?`という構文が使われていますが、これは元のオブジェクト型からオプショナル性（`?`）を取り除く機能です。これがないと`{ hoge?: "hoge"; }`のようになり`hoge`の型が`"hoge" | undefined`となるため、結果の型に`undefined`が混ざってしまいます。別の方法としては、最後に`NonNullable<...>`で`undefinend`を取り除くという方法もあります。

結局、このオブジェクトのプロパティの型全てのユニオン型を取ると、`"foo" | never | never | "hoge" | never`となり、これは`"foo" | "hoge"`に等しいので目的が達成できます。「プロパティの型全てのユニオン型を取る」の部分はLookup型を用いて`{ ... }[keyof Obj]`とすればできます。Lookup型は本来オブジェクトの指定したキーの型を得るための型ですが、キー名としてユニオン型を与えると、それらのキーを持つプロパティの型のユニオン型を得ることができます。例えば`Obj["foo" | "bar"]`は`Obj["foo"] | Obj["bar"]`です。これは、「キー名が`"foo"`かもしれないし`"bar"`かもしれない」と考えると、その値が「`Obj["foo"]`かもしれないし`Obj["bar"]`かもしれない」と解釈でき、自然ですね。

このように、mapped型を用いてキー名をオブジェクト型のプロパティの型のほうに持ってきてから操作し、最後にLookup型でユニオンを取るというテクニックは便利でよく使われます。

## 4-8. オプショナルなキーだけ抜き出す

```ts
type PickUndefined<Obj> = {
  [K in keyof Obj]-?: undefined extends Obj[K] ? K : never
}[keyof Obj];

type MapToNever<Obj> = {
  [K in keyof Obj] : never
}

type OptionalKeys<Obj> = PickUndefined<MapToNever<Obj>>
```

先ほどの問題の応用編のような問題です。`PickUndefined<Obj>`は4-7の`KeysOfType`と似ていますが、条件が`Obj[K] extends Val`ではなく`undefined extends Obj[K]`となっています。これは、`Obj[K]`が`undefined`型のときは満たされますが`never`型のときは満たされない条件です。

`MapToNever<Obj>`はmapped typeで全てのプロパティが`never`型となったオブジェクト型を作っています。
`OptionalKeys<Obj>`は、この2つの型を順番に適用するだけの型となっています。

なぜこれでうまく動くのかは、`MapToNever<Obj>`の結果を抜き出してみれば分かります。

```ts
/*
Oは
{
    foo: never;
    bar?: undefined;
    baz?: undefined;
    hoge: never;
    piyo?: undefined;
}
型
*/
type O = MapToNever<Data>;
```

このように、`MapToNever`の結果は、オプショナルなプロパティは`undefined`、それ以外のプロパティは`never`というオブジェクト型になっています。全て`never`に飛ばしたはずなのになぜ`undefined`が現れているのかという点が疑問ですが、これはオプショナルなプロパティの型には自動的に`undefiend`が付加されるという性質によるものです。`MapToNever`では、あえて`-?`を使わないことで、mapped type元のオブジェクトのオプショナルプロパティを敢えて引き継ぐようにしています。

このようにしてオプショナルプロパティとそれ以外のプロパティを区別することができたので、あとは`PickUndefined`で`undefined`のものを抜き出せば終わりです。