1歩進んだTypeScript - 型チートシート（＋サンプルコード）

この記事は[Ibrahima Ndaw](https://twitter.com/ibrahima92_)さんの[Advanced TypeScript Types cheat sheet (with example)](https://dev.to/ibrahima92/advanced-typescript-types-cheat-sheet-with-examples-5414)
を翻訳した記事です。

TypeScriptを勉強していて良い記事だと思ったのでTwitterで許可を頂いて翻訳しています。
翻訳するのは初めて＋TypeScriptも勉強中なため間違いがあれば指摘頂けると助かります。
ではここから翻訳です。

# 1歩進んだTypeScript - 型チートシート（＋サンプルコード）

TypeScriptは型付言語です。あなたは変数・関数の引数・関数の戻り値・オブジェクトのプロパティに型を指定することが出来ます。

ここでは進んだTypeScriptの型を例付きチートシートで示していきます。

- Intersection Types
- Union Types
- Generic Types
- Utility Types
    - Partial
    - Required
    - Readonly
    - Pick
    - Omit
    - Extract
    - Exclude
    - Record
    - NonNullable
- Mapped Types
- Type Guards
- Conditional Types

## Intersection Types

Intersection Typeは複数の型を1つの型に統合します。
つまり、あなたは型Aと型B、型C等の全てのプロパティを持つ1つの型にマージ出来ます。

```ts
type LeftType = {
  id: number
  left: string
}

type RightType = {
  id: number
  right: string
}

type IntersectionType = LeftType & RightType

function showType(args: IntersectionType) {
  console.log(args)
}

showType({ id: 1, left: "test", right: "test" })
// Output: {id: 1, left: "test", right: "test"}
```

見ての通り、`IntersectionType`は2つの型を統合した型です。
`LeftType`と`RightType`と`&`記号でintersection typeを作ります。

## Union Types

あなたはUnion Typeを使えば異なる型ヒントを1つの変数に指定できます。

```ts
type UnionType = string | number

function showType(arg: UnionType) {
  console.log(arg)
}

showType("test")
// Output: test

showType(7)
// Output: 7
```

`showType`関数はunion typeです。関数は文字列と数字を引数に受けます。

## Generic Types

Generic Typeは指定された方で関数・クラスを再利用する仕組みです。
サンプルコードでは`T`で渡された引数の型を補足します。

```ts
function showType<T>(args: T) {
  console.log(args)
}

showType("test")
// Output: "test"

showType(1)
// Output: 1
```

Generic Typeを作るとき、あなたはカッコ<>と引数として`T`を関数に渡さないといけません。
`T`（Tではなく、名前は何でも構いません）は異なる型ヒントで2回関数を呼びました。
関数はGeneric Typeなので再利用可能になりました。

```ts
interface GenericType<T> {
  id: number
  name: T
}

function showType(args: GenericType<string>) {
  console.log(args)
}

showType({ id: 1, name: "test" })
// Output: {id: 1, name: "test"}

function showTypeTwo(args: GenericType<number>) {
  console.log(args)
}

showTypeTwo({ id: 1, name: 4 })
// Output: {id: 1, name: 4}
```

ここにもう1つのサンプルコードを示します。
`GenericType`インタフェースはGeneric Type`T`を受けることが出来ます。
そしてshowTypeでは文字列を、showTypeTwoではnumberを使って型を再利用出来ました。

```ts
interface GenericType<T, U> {
  id: T
  name: U
}

function showType(args: GenericType<number, string>) {
  console.log(args)
}

showType({ id: 1, name: "test" })
// Output: {id: 1, name: "test"}

function showTypeTwo(args: GenericType<string, string[]>) {
  console.log(args)
}

showTypeTwo({ id: "001", name: ["This", "is", "a", "Test"] })
// Output: {id: "001", name: Array["This", "is", "a", "Test"]}
```

Generic Typeは複数の引数を指定できます。
ここでは`T`と`U`2つの引数を渡し、プロパティの型ヒントとして使っています。
つまり、私たちはそのインタフェース利用して、異なる型を引数として提供できます。

## Utility Types

TypeScriptは型を簡単に操作するための組み込みユーティリティを提供します。
使い方はあなたが変形したい型を`<>`で囲みます。

### Partial

`Partial<T>`

Partialは型`T`の全てのプロパティをnullableにします。
全フィールド名の最後に`?`マークを追加します。

```ts
interface PartialType {
  id: number
  firstName: string
  lastName: string
}

function showType(args: Partial<PartialType>) {
  console.log(args)
}

showType({ id: 1 })
// Output: {id: 1}

showType({ firstName: "John", lastName: "Doe" })
// Output: {firstName: "John", lastName: "Doe"}
```

上のサンプルコードの通り、`PartialType`インタフェースは`showType()`関数の型ヒント変数として使っています。
引数のプロパティをnullableへ変化するために`Partial`キーワードを使います。
そして`PartialType`型を引数として渡します。
その結果全てのフィールドがnullableになります。

### Required

`Required<T>`

`Partial`とは違ってに`Required`ユーティリティは`T`の全プロパティを必須にします。

```ts
interface RequiredType {
  id: number
  firstName?: string
  lastName?: string
}

function showType(args: Required<RequiredType>) {
  console.log(args)
}

showType({ id: 1, firstName: "John", lastName: "Doe" })
// Output: { id: 1, firstName: "John", lastName: "Doe" }

showType({ id: 1 })
// Error: Type '{ id: number: }' is missing the following properties from type 'Required<RequiredType>': firstName, lastName
```

`Required`ユーティリティを使う前はnullableだったオブジェクトも全て必須プロパティに変えます。
もし1つでもプロパティが省略されていた場合TypeScriptはエラーを投げます。

### Readonly

`Readonly<T>`

`Readonly`ユーティリティは`T`の全プロパティを再代入不可にします。

```ts
interface ReadonlyType {
  id: number
  name: string
}

function showType(args: Readonly<ReadonlyType>) {
  args.id = 4
  console.log(args)
}

showType({ id: 1, name: "Doe" })
// Error: Cannot assign to 'id' because it is a read-only property.
```

上のサンプルコードでは私達が`Readonly`ユーティリティで`ReadonlyType`インタフェースのプロパティを再代入不可にしています。
つまり、もしあなたが渡されたオブジェクトのフィールドを変更しようとしたら、エラーが発生します。

追記：`readonly`キーワードをプロパティの前に付けると再代入不可にできます。

```ts
interface ReadonlyType {
  readonly id: number
  name: string
}
```

### Pick

`Pick<T, K>`

`Pick`ユーティリティは`T`型からプロパティ`K`を選び出して新しい型を作ります。

```ts
interface PickType {
  id: number
  firstName: string
  lastName: string
}

function showType(args: Pick<PickType, "firstName" | "lastName">) {
  console.log(args)
}

showType({ firstName: "John", lastName: "Doe" })
// Output: {firstName: "John"}

showType({ id: 3 })
// Error: Object literal may only specify known properties, and 'id' does not exist in type 'Pick<PickType, "firstName" | "lastName">'
```

`Pick`はこれまでのユーティリティとは少し違います。
`Pick`は2つのパラメータを期待します。
`T`型から`K`で指定したプロパティを選んで新しい型を作ります。
上のサンプルコードの通り、あなたはパイプ（`|`）で複数のフィールドを選択できます。

### Omit

`Omit<T, K>`

`Omit`ユーティリティは`Pick`ユーティリティの反対です。
`T`型から`K`プロパティを選ぶのではなく取り除きます。

```ts
interface PickType {
  id: number
  firstName: string
  lastName: string
}

function showType(args: Omit<PickType, "firstName" | "lastName">) {
  console.log(args)
}

showType({ id: 7 })
// Output: {id: 7}

showType({ firstName: "John" })
// Error: Object literal may only specify known properties, and 'firstName' does not exist in type 'Pick<PickType, "id">'
```

`Omit`ユーティリティは`Pick`ユーティリティと似た働きをします。
`Omit`ユーティリティは基準にする型と取り除くプロパティを指定します。

### Extract

`Extract<T, U>`

`Extract`ユーティリティは2つの型から共通のプロパティを抽出します。
`Extract`ユーティリティは`T`型の全プロパティから`U`型で割り当て可能なプロパティを抽出します。

```ts
interface FirstType {
  id: number
  firstName: string
  lastName: string
}

interface SecondType {
  id: number
  address: string
  city: string
}

type ExtractType = Extract<keyof FirstType, keyof SecondType>
// Output: "id"
```

上のサンプルコードではidプロパティを共通に持つ2つの型を定義してます。
したがって`Extract`キーワードを使うことで2つの型から`id`フィールド抽出してます。
そしてもし`id`以外にも共通のプロパティがある場合、共通する全プロパティを抽出します。

### Exclude

`Exclude<T, U>`

`Extract`ユーティリティとは違い、`Exclude`ユーティリティは`T`型のプロパティから`U`型が持つプロパティを除外します。

```ts
interface FirstType {
  id: number
  firstName: string
  lastName: string
}

interface SecondType {
  id: number
  address: string
  city: string
}

type ExcludeType = Exclude<keyof FirstType, keyof SecondType>

// Output; "firstName" | "lastName"
```

上のサンプルコードでは`firstName`・`lastName`プロパティが`SecondType`型にはありません。
そして`Extract`キーワードを使うことで期待通りのフィールドを取りだせます。


### Record

`Record<K, T>`

`Record`ユーティリティであなたは`K`プロパティと`T`型のセットを構築できます。
`Record`は別のプロパティへマッピングするときに本当に使いやすいです。

```ts
interface EmployeeType {
  id: number
  fullname: string
  role: string
}

let employees: Record<number, EmployeeType> = {
  0: { id: 1, fullname: "John Doe", role: "Designer" },
  1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },
  2: { id: 3, fullname: "Sara Duckson", role: "Developer" },
}

// 0: { id: 1, fullname: "John Doe", role: "Designer" },
// 1: { id: 2, fullname: "Ibrahima Fall", role: "Developer" },
// 2: { id: 3, fullname: "Sara Duckson", role: "Developer" }
```

`Record`は比較的単純に働きます。
上のサンプルコードでは`number`を`employees`のキーとして0, 1, 2をキーとして期待しています。
そしてもしあなたが文字列をキーに使うとエラーが発生します。
次に`EmployeeType`を渡しているので、id, fullname, roleフィールドを持つオブジェクトが設定されます。

### NonNullable

`NonNullable<T>`

`NonNullable`ユーティリティは`T`型から`null`と`undefined`を取り除きます。

```ts
type NonNullableType = string | number | null | undefined

function showType(args: NonNullable<NonNullableType>) {
  console.log(args)
}

showType("test")
// Output: "test"

showType(1)
// Output: 1

showType(null)
// Error: Argument of type 'null' is not assignable to parameter of type 'string | number'.

showType(undefined)
// Error: Argument of type 'undefined' is not assignable to parameter of type 'string | number'.
```

上のサンプルコードで私たちは`NonNullableType`型を`NonNullable`ユーティリティの引数に渡して`null`と`undefined`を取り除いた新しい型を作ります。
つまり`null`や`undefined`を割り当てようとするとTypeScriptはエラーを投げます。

ところで`--strictNullChecks`フラグを`tsconfig`ファイル追加するとTypeScriptはnon-nullabilityルールを適用します。

## Mapped Types

Mapped Typesを使うと、あなたは存在するオブジェクトからそれぞれのプロパティを新しい型に変形出来ます。
メモ：今までに上げたいくつかのユーティリティはMapped Typesでもある。

```ts
type StringMap<T> = {
  [P in keyof T]: string
}

function showType(arg: StringMap<{ id: number; name: string }>) {
  console.log(arg)
}

showType({ id: 1, name: "Test" })
// Error: Type 'number' is not assignable to type 'string'.

showType({ id: "testId", name: "This is a Test" })
// Output: {id: "testId", name: "This is a Test"}
```

`StringMap<>`は受け取った型のプロパティを全てstringに変えます。
つまり私たちがもし`showType()`関数を呼ぶならすべてのフィールドをstringにしなければなりません。
そうでなければTypeScriptはエラーを投げます。

## Type Guards

Type Guardsはオブジェクトや型をチェックします。
`typeof`, `instanceof`, `in`を使い条件分岐します。

- `typeof`

```js
function showType(x: number | string) {
  if (typeof x === "number") {
    return `The result is ${x + x}`
  }
  throw new Error(`This operation can't be done on a ${typeof x}`)
}

showType("I'm not a number")
// Error: This operation can't be done on a string

showType(7)
// Output: The result is 14
```

上のサンプルコードの通り私たちはJavaScriptで`typeof`を使い条件ブロックを作ることが出来ます。
この場所で引数が条件の型と一致しているかチェックできます。

- `instanceof`

```ts
class Foo {
  bar() {
    return "Hello World"
  }
}

class Bar {
  baz = "123"
}

function showType(arg: Foo | Bar) {
  if (arg instanceof Foo) {
    console.log(arg.bar())
    return arg.bar()
  }

  throw new Error("The type is not supported")
}

showType(new Foo())
// Output: Hello World

showType(new Bar())
// Error: The type is not supported
```

1個前のサンプルコードの様に引数が`Foo`クラスかどうかをチェックしメソッドを呼びます。

- `in`

```ts
interface FirstType {
  x: number
}
interface SecondType {
  y: string
}

function showType(arg: FirstType | SecondType) {
  if ("x" in arg) {
    console.log(`The property ${arg.x} exists`)
    return `The property ${arg.x} exists`
  }
  throw new Error("This type is not expected")
}

showType({ x: 7 })
// Output: The property 7 exists

showType({ y: "ccc" })
// Error: This type is not expected
```

上のサンプルコードでは`in`演算子であなたは引数に`x`を持つかをチェックしています。

## Conditional Types

2つの型をテストして、その結果によって型を選択します。

```ts
type NonNullable<T> = T extends null | undefined ? never : T
```

上のサンプルコードは`NonNullable`ユーティリティ型でnullかどうかをチェックします。
そしてお気づきでしょうけどJavaScriptの3項演算子を使います。

読んでくれてありがとうございます。