TypeScriptの型初級

この記事は「TypeScriptの型入門」の続編です。入門の続編ということなので初級というタイトルにしてみました。TypeScriptの型よくわからんという方は先に入門から読むことをおすすめします。入門レベルのTypeScriptくらい分かるよという方は読まなくても大丈夫です。

- [TypeScriptの型入門](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a)

さて、前回の記事ではTypeScriptの型を一通り紹介しました。この記事ではその続編として、実用上必要になるTypeScriptの型の挙動を理解したり、標準ライブラリに存在する型の使い方を理解することを目標にします。前回に引き続き、あくまでTypeScriptの**型**に関する話ですから、JavaScriptの言語機能とか、TypeScriptの構文とかの話はしません。悪しからずご了承ください。

最終更新： 2019-03-16 (TypeScript 3.4に対応しました）

# union型の復習

実は初級編のひとまずの主役は**union型**です。ですので、union型について復習しておきましょう。

union型は、`T1 | T2 | T3`のように複数の型を`|`でつなげた型で、意味は「`T1`, `T2`, `T3`のいずれかである値の型」となります。例えば、`string | number`は`string`または`number`である値の型、すなわち「文字列または数値」という型です。

```ts
// 文字列はstring | number型に代入可能
const val1: string | number = 'foo';

// 数値もstring | number型に代入可能
const val2: string | number = 123;

// それ以外はだめ（エラー）
const val3: string | number = {foo: 'bar'};
```

union型のいいところは、if文やswitch文などで実行時に型を判定するコードを書くと、それに合わせて型が絞られる点です。

```ts
function func(arg: string | number) {
  if ('string' === typeof arg) {
    // 実行時にargが文字列であることを確認したので
    // この中ではargはstring型
    console.log(arg.length);
  } else {
    // argはstringではないので
    // この中ではargはnumber型
    console.log(arg * 10);
  }
}
```

特に、代数的データ型っぽいパターンの型に対してもこの機能が有効に働きます。

```ts
type None = { type: 'None' };
type Some<T> = { type: 'Some'; value: T };
type Option<T> = None | Some<T>;

function map<T, U>(opt: Option<T>, func: (value: T) => U): Option<U> {
  if (opt.type === 'Some') {
    // この中ではoptは { type: 'Some'; value: T } 型
    const newValue = func(opt.value);
    return { type: 'Some', value: newValue };
  } else {
    // この中ではoptは { type: 'None' } 型
    return { type: 'None' };
  }
}
```

以上がunion型の復習でした。それでは、最初のテーマに入っていきます。

# conditional typeにおけるunion distribution

TypeScriptの型入門を読んだ皆さんは、conditional typeの基本を既にご存知かと思います。余談ですが、conditional typeの訳語は条件型でいいのでしょうか。

実は、conditional typeには、TypeScriptの型入門の記事では紹介していなかった重要な性質があります。それが**union distribution**です。日本語に直すとunion型の分配でしょうか。

とりあえず、conditional typeの例を見ましょう。

```ts
type None = { type: 'None' };
type Some<T> = { type: 'Some'; value: T };
type Option<T> = None | Some<T>;

/**
 * ValueOfOption<V>: Option<T>を受け取って、渡されたのがSome型なら中身の値の型を返す。
 * 渡されたのがNone型ならundefinedを返す。
 */
type ValueOfOption<V extends Option<unknown>> = V extends Some<infer R> ? R : undefined;

const opt1: Some<number> = { type: 'Some', value: 123 };

// typeof opt1はSome<number>なので
// ValueOfOption<typeof opt1>はnumber
const val1: ValueOfOption<typeof opt1> = 12345;

const opt2: None = { type: 'None' };

// typeof opt2はNoneなので
// ValueOfOption<typeof opt2>はundefined
const val2: ValueOfOption<typeof opt2> = undefined;
```

`ValueOfOption<V>`は、`V`に渡された型が`Some<R>`型（の部分型）だった場合はその`R`を返し、そうでない場合は`undefined`を返すような型となります。

ところで、`V`として`Option<T>`型を渡したらどうなるでしょうか。まず、`Option<T>`型は`Some<T>`型の部分型ではありません。なぜなら`Option<T>`型の値は`None`型である可能性があり、それは`Some<T>`ではないからです。では、conditional typeの定義に従って`undefined`となるのでしょうか。

しかし、**実はそうではありません**。ここからがこの記事の新しいところです。

実際にやってみると、実は`ValueOfOption<Option<T>>`は`T | undefined`となります。

```ts
type None = { type: 'None' };
type Some<T> = { type: 'Some'; value: T };
type Option<T> = None | Some<T>;

type ValueOfOption<V extends Option<unknown>> = V extends Some<infer R> ? R : undefined;

// T1は number | undefined となる
type T1 = ValueOfOption<Option<number>>;

const val1: T1 = 123;
const val2: T1 = undefined;
```

条件型はどちらか片方を返すはずなのに、まさかの両方とは、これは反則もいいところです。この動作を説明するのが**union distribution**なのです。

今回のポイントは、条件型の条件部分の型`V`が`Option<T>`というunion型になっている点です。`Option<T>`は`None | Some<T>`というunion型でしたね。このように条件型の条件部分にunion型が来たときに、条件型は特殊な動作をします。一言で説明すると、「union型の条件型」が「条件型のunion型」に変換されます。数学等ではこのような挙動をdistribution（分配）といいますから、union distributionというのもそこから来ています。

今回の例で具体的に説明すると、`V`に`Option<T>`、すなわち`None | Some<T>`が入りますから、条件型の`V`のところに`None`と`Some<T>`がそれぞれ入った2つの条件型が生成され、それのunionになります。すなわち、`V extends Option<infer R> ? R : undefined`は`(None extends Some<infer R> ? R : undefined) | (Some<T> extends Some<infer R> ? R : undefined)`に変換されます。

これを計算すると確かに`undefined | T`となりますね。これが条件型におけるunion distributionの基本です。この挙動には、2つほど注意しなければいけない点があります。

## 条件型の結果側における型変数の置き換え
下に示す別の条件型を考えてみましょう。

```ts
type NoneToNull<V extends Option<unknown>> = V extends Some<unknown> ? V : null;
```

この`NoneToNull<V>`型は、`V`が`Some<T>`ならそのままで、`None`なら`null`に変換するという型です。先ほどの条件型との大きな違いは、条件部分だけでなく結果部分にも`V`が表れているということです。この`V`に対してunion distributionが発生して`V`が置換されるとき、**結果部分の`V`も同時に置換されます**。

`NoneToNull<Option<T>>`の場合は、これは`(None extends Some<unknown> ? None : null) | (Some<T> extends Some<unknown> ? Some<T> : null)`に変換され、結果は`null | Some<T>`となります。ポイントは、分配後の条件型で、もともと`V`だったところが左と右でそれぞれ`None`と`Some<T>`に置換されているところです。

## 分配されるのは型変数のみ
もう1つ注意しなければいけない点があり、これが条件型の大変ややこしいところでもあります。それは、**今まで説明したようなunion distributionが発生するのは条件部分の型が型変数である場合のみである**という点です。

今までのサンプルでは、条件型の`extends`の左が全部`V`だったことを思い出してください。この`V`は`ValueOfOption<V>`のように型の引数として導入された型変数です。このように、`extends`の左が型変数ただひとつという形であるときしか、union distributionは発生しないのです。

例えば、`ValueOfOption<V>`を使わずに、いきなり`Option<number> extends Some<infer R> ? R : undefined`という型を書いてみたらどうなるでしょうか。これは`extends`の左が型変数ではないので、union distributionが発生しません。よって、結果は`undefined`です。

```ts
type None = { type: 'None' };
type Some<T> = { type: 'Some'; value: T };
type Option<T> = None | Some<T>;

// T1はundefined
type T1 = Option<number> extends Some<infer R> ? R : undefined;

const val1: T1 = undefined;
// ↓これはエラーになる
const val2: T1 = {type: 'Some', value: 123};
```

このように、型関数（型引数を持つような型をこう呼んでいます）をインライン化するだけで結果が変わるというのは非直感的ですね。また、union distributionを使いたいときは必ずそこを型変数にする必要があり、すなわち型関数を作る必要があります。自分で型を書くときだけでなく、人が書いたTypeScriptの型を読むときにも、条件型が出てきたらこれはunion distributionをさせることを意図しているのか、それともさせないことを意図しているのかを考えなければ読まなければいけません。

逆に型変数で条件分岐したくなったけどunion型が来ても分配してほしくない場合のテクニックとしては、何か適当な型で囲むというものがあります。配列型で囲むのが、記法が簡単なのでよく使われます。

```ts
type None = { type: 'None' };
type Some<T> = { type: 'Some'; value: T };
type Option<T> = None | Some<T>;

type ValueOfOption<V> = V[] extends Some<infer R>[] ? R : undefined;

// これはnumber型
const val1: ValueOfOption<Some<number>> = 123;
// これはundefined型
const val2: ValueOfOption<None> = undefined;
// これはnumber | undefinedではなくundefined型
const val3: ValueOfOption<Option<number>> = undefined;
// ↓なのでこれはエラー
const val4: ValueOfOption<Option<number>> = 123;
```

この例では、条件部分に来ている`V[]`はただの型変数ではないのでunion distributionの発生条件に当てはまらず、分配が発生しません。

## never型とunion distribution
`never`型は属する値が無い型でしたが、union distributionに際して少し特殊な振る舞いをします。`never`型は、0個のunion型であるように振る舞います。例として次のサンプルを見てみましょう。

```ts
type IsNever<T> = T extends never ? true : false;

// T1はneverになる
type T1 = IsNever<never>;
```

この例で、`IsNever<T>`は`T`が`never`ならば`true`になりそうでなければ`false`になる型というつもりでしたが、`never`を渡した結果は`true`でも`false`でもなく`never`です。これは`never`が0個のunionのように振る舞うことから説明できます。言い換えれば、`T`が型変数で`T extends never ? X : Y`という形の条件型に対して`T`に`never`を代入すると常に結果は`never`になります。

これを避ける方法はついさっき説明したばかりなので省略します。


## union distributionのまとめ
ここで説明したことを一言でまとめ直すと、「条件型の条件部分の型が型変数ならばunion型が分配される」ということです。

この動作は型で条件分岐するものとしての条件型の直観からは外れてしまいますが、この挙動によってunion型をたいへん便利に扱えるようになります（これについてはこの記事の後半で取り扱います）。また、最後に紹介したように、本当に型で条件分岐させたいときの方法（`extends`の左が型変数だけになるのを避ける）も残されていますから、まあ良いのではないかと思います。

`extends`の左が型変数になるのを特別扱いする理由としては、unionを分配したときに結果側も書き換えなければいけないことが挙げられます。下の例を思い出して欲しいのですが、この例では`V`に入ったunion型が分配されることになるので、結果側も`V`を書き換えればいいことが明らかです。型変数に入っていないunion型を分配しようとすると右辺に出現する同じ型を適切に書き換える必要が出てきますが、それは困難です。

```ts
type NoneToNull<V extends Option<unknown>> = V extends Some<unknown> ? V : null;
```

ただ、この「型が型変数かどうかで挙動が変わる」という性質は非直感的でたいへん厄介ですから、しっかり覚えておきましょう。

# mapped typeのunion distribution
実は、mapped typeもunion型を分配します。分配が発生する条件は条件型のときよりも複雑で、以下の形のmapped typeで`T`が型変数のときに、`T`にunion型が入ると分配されます。（`X`は何らかの型で、型変数でなくても構いません。）

```ts
{[P in keyof T]: X}
```

実際にやってみましょう。下で定義する`Arrayify<T>`は`T`の全てのプロパティを配列化するmapped typeです。

```ts
type Arrayify<T> = {[P in keyof T]: Array<T[P]>};

type Foo = { foo: string };
type Bar = { bar: number };

type FooBar = Foo | Bar;

// FooBarArrはunion型が分配されてArrayify<Foo> | Arrayify<Bar>になる
type FooBarArr = Arrayify<FooBar>;

const val1: FooBarArr = { foo: ['f', 'o', 'o'] };
const val2: FooBarArr = { bar: [0, 1, 2, 3, 4] };
// ↓これはArraify<Foo>でもArraify<Bar>でもないのでエラー
const val3: FooBarArr = {};
```

mapped typeが分配されていることが分かりました。

試しに、以下のように`Arraify<T>`を介さずにmapped typeを使ってみると結果が変わります。これにより、今回もやはり型変数が条件になっていることが分かります。

```ts
type Foo = { foo: string };
type Bar = { bar: number };

type FooBar = Foo | Bar;

// FooBarArrは{}になる
type FooBarArr = {[P in keyof FooBar]: Array<FooBar[P]>};
// ↓これがエラーにならない！
const val1: FooBarArr = {};
```

上の例では、`keyof FooBar`は`"foo" & "bar"`という型になります。これは`"foo"`でありかつ`"bar"`であるという意味であり、そんな値は存在しないので`never`になってほしいですが、どうもならないようです。とにかく、`Foo`と`Bar`に共通する名前のプロパティは存在しないため、`FooBar`に存在する（確実に存在すると言える）プロパティはありません。よって`keyof FooBar`に当てはまるような`P`が無いため`FooBarArr`は`{}`と計算されます。

このようなmapped typeによるunionの分配は、`in`の右側が`keyof T`（しかも`T`は型変数）で`T`にunion型が入るという条件を満たすときにのみ発生します。狙ってやるなら問題ありあませんが、条件型のunion distributionよりこころなしか知名度が低いような気もしますので、ときには罠と化すことがあるかもしれません。

`[P in keyof T]`という形そのものは`T`のプロパティを全部マップするときに使う頻出の形です。この形が出てきたときは`T`がunion型だったらどうなるかなということに思いを馳せる必要があるでしょう。


# mapped typeと配列型
上と同じ`[P in keyof T]`という形で、`T`に配列の型（タプル型も含む）が入る場合も、やはり特別な挙動をします。

そもそも、配列の型にmapped typeを適用するとどうなるでしょうか。恐らく、とりあえず思い浮かべる挙動は、配列の要素の型がマップされるというものだと思います。まずは、敢えて特別な挙動を避けつつ配列の型をマップしてみましょう。`StrArr`型は`NumArr`のプロパティの型を文字列に変更した型を返します。

```ts
type NumArr = number[];

type StrArr = { [P in keyof NumArr]: string };

// StrArr型の変数aを宣言
declare const a: StrArr;

const _: string = a[0];
```

この例では、`in`の右が`keyof NumArr`になっていますが、`NumArr`は型変数ではなく具体的な型なので特別な挙動は発生しません。ちゃんと、`StrArr`型の配列の要素は文字列になっていますね。

では、配列なのでforEachでループを回してみましょう。

```ts
type NumArr = number[];

type StrArr = { [P in keyof NumArr]: string };

// StrArr型の変数aを宣言
declare const a: StrArr;

// エラー: Cannot invoke an expression whose type lacks a call signature. Type 'String' has no compatible call signatures.
a.forEach(val => {
  console.log(val);
});
```

おや……？

そう、mapped typeにより全てのプロパティが文字列にマップされたため、本来関数型のはずの、配列が持つプロパティ`forEach`の型も`string`にされてしまったのです。

確かにそういうコードを書いたとはいえ、こんなのはさすがに使いものになりませんね。

ということで、この事態を避ける機構がmapped typeには組み込まれています。具体的には、`[P in keyof T]`で型変数`T`の型が配列だった場合に、全てのプロパティをマップするのではなく要素の型のみをマップしてくれるのです。ではやってみましょう。

```ts
// すべてのプロパティをstringにする型関数
type Strify<T> = {[P in keyof T]: string};

type NumArr = number[];
// StrArrはstring[]型になる
type StrArr = Strify<NumArr>;

const arr: StrArr = ['foo', 'bar'];
arr.forEach(val => console.log(val));
```

今度はちゃんと`StrArr`が`string[]`型となり、forEachなどのメソッドはそのまま使えるようになっています。

ポイントは、`Strify<T>`はごくふつうのmapped typeで定義されており、オブジェクトの型に対してもそのまま使えるという点です。配列を特別扱いしなくても、いい感じにマップしてくれるというわけですね。

ちなみに、この機能が`T`が型変数の場合に制限されているのは、そうしないとマップ後の配列の要素の型を正しく求められない場合があるからでしょう。`{[P in keyof T]: X}`という型（`X`は型変数とは限らない任意の型）で配列型`U[]`をマップした場合、要素の型は`X`内の`T[P]`を`U`で置換したものになります。この形のmapped typeならば、要素の型をマップする際に`X`の内部にもともとの要素の型はシンタクティックに`T[P]`という形で現れるためそれを`U`に置換すればいいわけです（ほかに`T[number]`とかもあるかもしれませんが、それは置換せずそのままでOKです）。`T`が一般の型になってしまうとこのような変換が難しくなります。

また、配列型と述べましたが、タプル型も配列型の一種ですので同じルールが適用されます。

```ts
// すべてのプロパティを配列にする型関数
type PropArrify<T> = {[P in keyof T]: Array<T[P]>};

type MyTuple = [string, number, boolean];

// T1は [string[], number[], boolean[]]になる
type T1 = PropArrify<MyTuple>;

const t: T1 = [ ['f', 'o', 'o'], [], [true, false]];

// lengthなどの要素以外のプロパティはそのままになっている
console.log(t.length);
```

## `readonly`配列型との変換
Mapped typeでは、プロパティを`readonly`化したりすることもできました。配列に対して全てのプロパティを`readonly`化する操作を行った場合、ちゃんと`readonly`配列やタプルの型が発生します。もちろん、上記の場合と同じように`[P in keyof T]`という形が必要です。

ちょっと話題を先取りしますが、TypeScriptには以下で定義される`Readonly<T>`型が標準で備わっています。これを使って試してみましょう。

```ts
type Readonly<T> = {readonly [P in keyof T]: T[P]};
```

下の例のように、`Readonly<T>`の型変数`T`に配列やタプルの型を入れると、`readonly`配列や`readonly`タプルの型になります。

```ts
type T1 = number[];

// T2 は readonly number[] 型
type T2 = Readonly<T1>;

// T3 は readonly [string, number, string] 型
type T3 = Readonly<[string, number, string]>;
```

逆に`{-readonly [P in keyof T]: T[P]}`によってプロパティから`readonly`を取り除くことができるのはご存知の通りですが、これを`readonly`配列型に適用するとやはり普通の配列型になります。

# 標準ライブラリの型
さて、ここまででconditional typesとmapped types、そしてunion型（あと配列）にまつわる細かい挙動を見てきました。特にunion型に対して例外的な挙動があったり、型変数を含んだ特定の形であるかどうかを気にしないといけなかったりと煩雑ですが、これによりTypeScriptの型システム（特にunion型）が便利なものになっているという側面もあります。

そのような便利さというのは、実は我々が直接conditional typesなどと弄れなくても標準ライブラリという形で提供されています。簡単な操作なら、標準ライブラリで提供されている型を用いて行うことができるでしょう。ここでは、標準ライブラリに存在する型を紹介します。標準ライブラリに存在するということは、特に何もせずともいきなり使ってよいということです。

なお、これらの型は[lib.es5.d.ts](https://github.com/Microsoft/TypeScript/blob/v3.1.3/lib/lib.es5.d.ts)で定義されており、以下で示す定義は同ソースコード（TypeScript v3.1.3）からの転載であり、[Apache License 2.0](https://github.com/Microsoft/TypeScript/blob/v3.1.3/LICENSE.txt)に従います。

## `Record<K, T>`
```ts
/**
 * Construct a type with a set of properties K of type T
 */
type Record<K extends keyof any, T> = {
    [P in K]: T;
};
```

`Record<K, T>`型は、辞書として使いたいオブジェクトの型に適しています。例えば`Record<string, number>`は、任意の文字列型のキー（プロパティ名）に対して`number`型の値を持ったオブジェクトです。

```ts:Recordの使用例
const dict: Record<string, number> = {};
dict.foo = 123;
dict.bar = 456;
console.log(dict.foo + dict.baz);
```

ただ、上の定義だと、存在しないキーがundefinedを返す可能性を無視していることに注意してください。これが気に入らない場合は`Record<string, number | undefined>`のように値がundefinedである可能性を明示するか、`Map`を使うようにします。この型はそのような危険性よりも利便性を重視する場合に使われる型であるという印象です。

また、`K`の制約にある`K extends keyof any`に注意してください。TypeScriptでは、オブジェクトのキー（プロパティ名）として使用できる型は決まっており、具体的には`string | number | symbol`です。ですから、`keyof 型`という型はかならず`string | number | symbol`の部分型になります。`keyof any`はキーとしてありえる全ての型となり、やはり`string | number | symbol`になります。最初から`string | number | symbol`と書いてもよいですが、将来的にキーになり得る型が追加されたときのためかこれをベタ書きするのは避けて`keyof any`としているようです[^keyof_any]。

[^keyof_any]: 実際、TypeScript 2.9より前は`keyof any`は`string`になっていました。

## `Partial<T>`, `Required<T>`, `Readonly<T>`

```ts
/**
 * Make all properties in T optional
 */
type Partial<T> = {
    [P in keyof T]?: T[P];
};

/**
 * Make all properties in T required
 */
type Required<T> = {
    [P in keyof T]-?: T[P];
};

/**
 * Make all properties in T readonly
 */
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};
```

普通にmapped typeを使ってプロパティの性質を操作するだけの型です。`Partial<T>`は結構よく使います。

## `Pick<T, K>`

```ts
/**
 * From T pick a set of properties K
 */
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};
```

この型定義を見て何をやっているのか分かればTypeScript入門レベルは問題なく理解できていると言えるかもしれません。

これは、与えられたオブジェクトの型`T`のプロパティのうち、`K`で与えられた名前のプロパティのみを残したような型を返す型関数です。

```ts:Pickの使用例
interface MyObj {
  foo: number;
  bar: string;
  baz: boolean;
}

type T1 = Pick<MyObj, 'foo' | 'bar'>
/*
 * T1の型は
 * {foo: number; bar: string}
 */
```
残すべきプロパティ名をリテラル型のunion型で与えます。こういうところにTypeScriptにおけるunion型の重要性が見え隠れしています。

この`Pick<T, K>`型は既存の型をちょっといじった新しい型を作りたい場合によく使います。

## `Exclude<T, U>`, `Extract<T, U>`

```ts
/**
 * Exclude from T those types that are assignable to U
 */
type Exclude<T, U> = T extends U ? never : T;

/**
 * Extract from T those types that are assignable to U
 */
type Extract<T, U> = T extends U ? T : never;
```

これは何をやっているのか分かるでしょうか。これが分かれば、今回の話もだいぶ理解できています。

これは、conditional typeにおけるunion distributionを前提とした型です。`T`に何らかのunion型が入ったとき、`Extract<T, U>`は、その構成要素のうち`U`の部分型であるもののみを残します。

具体例を見ましょう。

```ts
type T1 = 'foo' | 'bar' | 'baz' | 0 | 2 | 4 | false;

// T2は 'foo' | 'bar' | 'baz' 型になる
type T2 = Extract<T1, string>;
```

`T1`はいろいろな型のunion型ですが、`T2`はそれらのうち文字列であるもの、すなわち`string`の部分型であるものだけを残したunion型になっています。

こうなる理由は、この記事に書いてあることで説明できます。`Extract<T1, string>`の計算においては、`ExtractT, U>`の中の`T extends U`の`T`にunion distributionが適用され、

```ts
('foo' extends string ? 'foo' | never) | ('bar' extends string ? 'bar' | never) | ...
```

のように分配されます。その結果は、`'foo' | 'bar' | 'baz' | never | never | never | never`となります。`never`は属する値が無い型ですから、union型の中では消えます。よって`'foo' | 'bar' | 'baz'`が残ったのです。

今回の説明は`Extract<T, U>`を使いましたが、`Exclude<T, U>`も同様の動作をします。違いは、`Exclude<T, U>`は逆に`U`の部分型であるものが除かれてそれ以外が残る点です。`Exclude<T1, string>`は`0 | 2 | 4 | false`型になるでしょう。

これらの型は、union型で代数的データ型っぽいことをやっている場合も役に立ちます。次のような型を考えます。

```ts
type MyData =
  | {
      type: 'foo';
      fooValue: string;
    }
  | {
      type: 'bar';
      barValue: number;
    }
  | {
       type: 'baz';
    };
```

`MyData`は`type`プロパティをタグとして、3種類の値からなる代数的データ型っぽい型です。

このうち、`'foo'`はもう処理したので残りは`'bar'`か`'baz'`であるという状況を表現するために、`MyData`から`type`が`'foo'`であるものを除いた残り2種類のunion型を作りたいことがあります。このとき、union型の定義をもう一度書き直すのは無駄ですね。`Exclude<T, U>`を使って次のように表現できます。

```ts
type T1 = Exclude<MyData, { type: 'foo' }>;
// T1は { type: 'bar'; barValue: number } | { type: 'baz' } 型
```

特に、`U`にあたる部分が`{type: 'foo'}`でいいのが特徴的で、`{type: 'foo'; fooValue: string}`のようにフルに書き下さずに必要最低限の条件で済んでいます。これは条件型の条件判定が`extends`、すなわち部分型関係を使っているからですね。

他の使い方としては、`Pick<T, K>`と組み合わせて、`T`から特定のキーだけ取り除くという使い方ができます。

```ts
interface MyObj {
  foo: number;
  bar: string;
  baz: boolean;
}

// T1は {foo: number; bar: string} 型
type T1 = Pick<MyObj, Exclude<keyof MyObj, 'baz'>>;
```

これはなかなかよく使うので、これに`Omit<T, K>`みたいな名前を付けることも結構あるようです。

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


## `NonNullable<T>`
```ts
/**
 * Exclude null and undefined from T
 */
type NonNullable<T> = T extends null | undefined ? never : T;
```

これは`Exclude<T, U>`の`U`が`null | undefined`になった版です。

## `Parameters<T>`, `ReturnType<T>`
```ts
/**
 * Obtain the parameters of a function type in a tuple
 */
type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;

/**
 * Obtain the return type of a function type
 */
type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
```

この2つは関数に関わる型です。`T extends (...args: any[]) => any`という条件は`T`が関数の型でなければいけないということを言っています。

`T`が関数のとき、`Parameters<T>`は`T`の引数の型一覧をタプル型の形で得ることができます。`ReturnType<T>`は`T`の返り値の型となります。

```ts:ParameterとReturnTypeの使用例
type F = (arg1: string, arg2: number) => string;

// T1は[string, number]型
type T1 = Parameters<F>;

// T2はstring型
type T2 = ReturnType<F>;
```

この2つの型は関数の型をごちゃごちゃといじりたい場合に便利ですね。

省略しますが、`new`で呼び出す場合用に同じことができる`ConstructorParameters<T>`と`InstanceType<T>`もあります。

---

以上が標準ライブラリで提供されている主な型です。特に、条件型のunion distributionを駆使した`Extract<T, U>`や`Exclude<T, U>`の定義が特徴的ですね。これらとunion型を使いこなしてTypeScriptプログラムを書くことができてばTypeScriptの型初級は卒業でしょう。

# その他のトピック
ここからは、[TypeScriptの型入門](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a)からは漏れてしまったTypeScriptの型に関する雑多な話題を紹介します。

## 関数のオーバーロード
TypeScriptでは、関数のオーバーロードを定義することができます。とはいえ、変換先のJavaScriptにその機能はありませんから、オーバーロードが書けるのは**型だけ**です。型でオーバーロードが定義されている関数`func`を書いてみます。

```ts:関数のオーバーロードの例
function func(arg1: number): number;
function func(arg1: string, arg2: number): number;
function func(arg1: number | string, arg2?: number): number {
  if ('number' === typeof arg1) {
    return arg1;
  } else {
    return Number.parseInt(arg1) + arg2!;
  }
}

func(1);
func("123", 321);
// ↓エラー
func("123");
```

これはfunction宣言が連続していてちょっと読みにくいですね。関数`func`の宣言は、型の宣言と本体の宣言の2つの部分に分けることができます。よく見ると、最初の2行は普通の関数宣言ではなく、関数本体が無い形ですね。これはTypeScriptに特有の形で、この形を複数並べることにより関数のオーバーロードを宣言します。この例では2つのオーバーロードが宣言されており、1つ目は数値を受け取って数値を返す関数、もう1つは文字列と数値を受け取って数値を返す関数です。

そして、オーバーロードの宣言に伴って、関数本体の宣言を書く必要があります。TypeScriptにおける関数のオーバーロードの特徴としては、関数の本体を1つしか書くことができないということです[^overload_limit]。なので、関数本体の宣言では、型はオーバーロードされた関数の全てに当てはまる包含的な型でなければいけません。

[^overload_limit]: これは、TypeScriptからJavaScriptに変換するときに型に依存してはいけないという制限によるものだと考えられます。引数の型を調べてどの実装を使うか選択するみたいな実装をTypeScriptが出力するわけにはいかないので、1つの実装で全てやる必要があります。

つまり、この関数`func`においては、1つ目の引数の型は`number`かもしれないし`string`かもしれないので、関数本体の宣言においてはその両方を受け入れる`number | string`とします。第2引数は`number`ですが、1つ目のオーバーロードが採用された場合は第2引数が無いので、無いかもしれないことを表すために`arg2?: number`とします。`?`をつけたことで`arg2`の型は`number | undefined`となります。

関数内部では、自分で引数の型を調べてそれに合わせて適切な実装を選択する必要があります。else側の`arg2!`が目に付きますが、この`!`後置演算子はTypeScript独自のもので、型から強制的に`null`や`undefined`を取り除くダウンキャストの演算子です。`arg1`が文字列であると判明した時点でオーバーロードの2番目が選択されていることは明らかなので、`arg2`がundefinedでないことは分かるのですが、残念ながらTypeScriptはそこまで賢くないので`arg2`がundefinedにならないことを見抜くことはできません。そのために、自分でアノテートしています。

このような使いにくさがあるので自分はあまりオーバーロードされた関数定義は好きではないのですが、在野のJavaScriptライブラリに型を付ける際によく登場します。

なお、オブジェクト型を用いて関数の型を宣言するときにも、同様に関数シグネチャを複数並べることでオーバーロードされた関数の型を表現できます。例えば、先ほど定義した関数の型を書くとこのようになります。

```ts
interface MyFunc {
  (arg1: number): number;
  (arg1: string, arg2: number): number;
}
```

## `this`
JavaScriptはオブジェクト指向言語なので、メソッドの中では`this`を使うことができます。このことをTypeScriptで表現するためのいろいろな工夫があります。

まず、実は関数定義や関数の型を書くときに、`this`の型を明示することができます。ややこしいことに、`this`の型は最初の引数に書いて明示しますが、これは本当の引数ではないため呼ぶときには`this`の値を引数として渡すわけではありません。下の例で定義する`func`の引数は`arg`の1つだけです。

```ts
type MyObj = { foo: number };
function func(this: MyObj, arg: number): number {
  return this.foo + arg;
}

const obj1 = {
    foo: 12345,
    func,
};
const obj2 = {
    func,
};

// funcを普通に呼ぶとthisの型が違うのエラー
func(100);

// obj1.funcとして呼ぶとthisはobj1 (MyObj型)なのでOK
obj1.func(100);

// obj2.funcとして呼ぶとthisがobj2でMyObj型でないのでエラー
obj2.func(100);
```

ここで定義したfuncは、`this`の型が`MyObj`でないといけないような関数型を持ちます。ですから、普通に呼んだり`this`が違う状況で呼ぶとエラーになります。上の例では、`obj1.func`として呼ぶ場合のみOKです。

ただ、こういう風に`this`を使っている場面は実際のところあまり見ません。`this`はどちらかというとcontextualな型推論のために使われることが多いように思います。jQueryに代表されるような一部のライブラリでは、（今はどうなのかよく分かりませんが少なくとも昔は）積極的にコールバック関数内での`this`の値を書き換えてきます。そのような関数の中において`this`の型を正しく推論させるために`this`の型指定を使うことができます。

```ts
type MyObj = { foo: number };

function callWithThis(func: (this: MyObj) => void): void {
  func.call({ foo: 42 });
}

//            ↓このコールバック関数の中ではthisがMyObj型を持つと推論される
callWithThis(function(){ console.log(this.foo); });
```

この例では、関数`callWithThis`の引数が関数であり、その型において`this`が`MyObj`であると指定されています。`callWithThis`の引数である無名関数の型はその型に当てはめられることになりますから、その中では`this`は`MyObj`型となるのです。このようにすることで、`callWithThis`を使う側ではコールバック関数内での`this`の型を自分で指定しなくてもカスタマイズされた`this`をうまく利用することができます[^contextual_infer_limit]。

[^contextual_infer_limit]: ただ、この無名関数を別の名前の関数としてくくり出すような場合は、その引数が`callWithThis`の引数として使われるという情報がないため、`this`の型を明示する必要があります。この辺はTypeScriptの型推論の弱い部分ですね。多分そういうデザインなのだと思いますが。

### `this`型

`this`に関わるTypeScriptの型としては、そのままずばりの`this`型という型があります。これは、クラス（やインターフェース）のメソッド内で使うことができる特殊な型です。例として、自分をコピーするメソッド`clone`を持ったクラスを考えます。

```ts
class MyClass {
  constructor(public foo: number) { }

  public clone(): MyClass {
    return new MyClass(this.foo);
  }
}
```

`MyClass`のコンストラクタは1つの引数を持ちます。引数に`public`とついていますが、このすると与えられた引数がそのままパブリックなプロパティ`foo`に代入されます（これはTypeScriptの独自機能です）。

まあそこは本題ではなく、本題は`clone()`メソッドです。これは自身と同じ新しいMyClassオブジェクトを返すメソッドです。そうなると、返り値の型は当然MyClass型です。

ここまでは問題ありませんが、このMyClassを継承した新しいクラスを作るときに少し困ります。

```ts
class MySubClass extends MyClass {
}
```

このままでは`MySubClass`インスタンスの`clone()`を呼び出すと`MySubClass`オブジェクトではなく`MyClass`オブジェクトが返ることになります。`MySubClass`の定義にオーバーライドされた`clone()`の定義を書きなおしてもよいですが、それは何だかスマートではありませんね。JavaScript的な解決策はこうです。（もちろん、常にこんな適当な実装で事が済むわけではないと思いますが。）

```ts
class MyClass {
  constructor(public foo: number) { }

  public clone(): MyClass {
    return new (this.constructor as any)(this.foo);
  }
}
```

インスタンスは`constructor`プロパティに自身のコンストラクタを持っています。ですので、それを持ってきてnewすればいいのです（TypeScriptがあまりかしこくないのでanyでむりやり何とかしていますが、まあ仕方ないのでそういうものだと思いましょう）。MyClassのインスタンスの場合は`this.constructor`はMyClassになり、MySubClassインスタンスの場合はMySubClassになります。

これでJavaScript的にはOKですが、TypeScript的にはまだ問題があります。返り値の型が`MyClass`で固定なのです。`MySubClass`の`clone()`を呼び出したときはちゃんと返り値が`MySubClass`型になってほしいですね。

もうお分かりかと思いますが、返り値を`this`にすればこれができます。`this`型は、文字通り`this`の型になります。今回は常にインスタンスの型と同じ型のオブジェクトが返されると考えてこのようにすればOKです。

```ts
class MyClass {
  constructor(public foo: number) { }

  public clone(): this {
    return new (this.constructor as any)(this.foo);
  }
}
```

完成形を見てもなんか無理やりだなと思った読者の方がいるかもしれませんが、`this`型を使わざるを得ない時点でそもそもわりと無理やりであるという説もあります。

## カスタム型ガード
if文と`typeof`等を組み合わせることによって、型の絞り込みを行うことができるのはご存知の通りです。実は、型の絞り込みを行う用の関数を自分で定義することができます。さっそくですが例を見てください。

```ts
type FooObj = { foo: number };

function isFooObj(arg: any): arg is FooObj {
  return arg != null && 'number' === typeof arg.foo;
}

function useFoo(arg: unknown) {
  if (isFooObj(arg)) {
    // この中ではargはFooObj型
    console.log(arg.foo);
  }
}

useFoo(123);
useFoo({foo: 456});
```

まず注目すべきは`isFooObj`の返り値の型です。`arg is FooObj`という型なのか何なのかよくわからんものになっていますが、これがカスタム型ガードです。構文は`引数名 is 型`で、その実態は真偽値です。返り値がtrueのときその引数はその型を持つことを保証するという意味になります。

`FooObj`は`number`型のプロパティ`foo`を持つオブジェクトの型なので、`isFooObj`では`arg`がその条件を満たすことを確かめています。

次に`useFoo`を見ましょう。この関数では、引数`arg`の型を`unknown`としています[^unknown_limit]。

[^unknown_limit]: 本当は`isFooObj`の引数の型も`unknown`にしたかったのですが、型エラーになってしまうのでちょっとずるいですが`any`にしています。まあ、型についての保証をする関数なのでその中身は自分で気をつけて書きましょうということです。

`arg`の型を`FooObj`型として使いたいのですが、そのためにはまず`arg`が`FooObj`型かどうか確かめる必要があります。そこで、if文の中で先ほど定義した`isFooObj`を呼び出します。この形で`isFooObj`を使うことによって、その中では`arg`の型が`FooObj`型に狭められるのです。

これも使いどころが沢山あるわけではないのですが、標準ライブラリ中の`Array.isArray`の定義にも使われています。

```ts:標準ライブラリのArrayConstructorの定義
interface ArrayConstructor {
    new(arrayLength?: number): any[];
    new <T>(arrayLength: number): T[];
    new <T>(...items: T[]): T[];
    (arrayLength?: number): any[];
    <T>(arrayLength: number): T[];
    <T>(...items: T[]): T[];
    isArray(arg: any): arg is Array<any>;
    readonly prototype: Array<any>;
}
```

# まとめ
前回の記事[TypeScriptの型入門](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a)は入門ということで型の説明に終始しましたが、この記事ではunion distributionを代表とするような、実用上理解しておく必要がある型の挙動を説明し、さらに標準ライブラリの有用な型もいくつか紹介しました。これらの知識を駆使して実用レベルでTypeScriptの力強い型システムを活かしていただきたいと思います。

特に、繰り返しになりますがTypeScriptの型システムにおいてはunion型が強力で、union型の利用を支援する機構も今回見たように整っています。もはやunion型が無いころのTypeScriptは何だったのかというレベルです。積極的にunion型を使っていきましょう。

また、入門レベルからのステップアップということで、記事の後半ではTypeScriptの型に関する少しマイナーな機能をいくつか紹介しました。マイナーというだけあって見る機会も使う機会も多くありませんが、ライブラリの型定義などを書くようになるとお世話になるかもしれません。

# 次のステップ

「TypeScriptの型中級」はまだ書いていませんが、その代わりに入門・初級をマスターしたみなさんが次に読むと良い記事をご案内します。

- [TypeScriptの型演習](https://qiita.com/uhyo/items/e4f54ef3b87afdd65546) 演習問題ができました。力試しに解いてみましょう。
- [TypeScriptの型推論詳説](https://qiita.com/uhyo/items/6acb7f4ee73287d5dac0) 型推論を詳しく理解したい方はこちら。
- [TypeScriptのreadonlyプロパティを使いこなす](https://qiita.com/uhyo/items/0fd033ff1aed9b4b32dd) `readonly`について深掘りする記事です。
- [TypeScriptで超型安全なBuilderパターン](https://qiita.com/uhyo/items/69341387ed6a493b5df5) TypeScriptの型の応用例です。
- [TypeScriptで配列が指定した要素を全部持つか調べる方法](https://qiita.com/uhyo/items/e9a03aa4ea2db8d1d7fe) TypeScriptの型の応用例です。
- [TypeScriptで最低一つは必須なオプションオブジェクトの型を作る](https://qiita.com/uhyo/items/583ddf7af3b489d5e8e9) これも応用例です。
- [TypeScriptで最低n個の要素を持った配列の型を宣言する方法](https://qiita.com/uhyo/items/80ce7c00f413c1d1be56) これまた応用例です。
- [TypeScriptの型レベル連結リスト活用術：型を変えられるコンテナを作る](https://qiita.com/uhyo/items/ab65e79f7b0c786406f6) さらに応用例です。