TypeScript③（ユーティリティタイプ）

### この記事について
この記事は私がTypeScriptについて学んだ内容を
[TypeScript①（基礎)](https://qiita.com/zgoovw_/items/7a6fcb18b92c6073f120)
[TypeScript②（型の機能）](https://qiita.com/zgoovw_/items/c44d28f3cd2fdf91219f)
TypeScript③（ユーティリティタイプ）
の3回に分けて網羅的に紹介していきます。

①（基礎）では導入方法と様々な型について簡単な紹介
②（型の機能）では型が実際にどういう働きをするのかについて

今回は、型変換を容易にするユーティリティ型についてです。

### 参考リンク

- [TypeScript公式ドキュメント](https://www.typescriptlang.org/docs/)

# ユーティリティタイプ
TypeScriptは、一般的な型変換を容易にするいくつかのユーティリティ型を提供します。これらのユーティリティはグローバルに利用できます。

### Partial型

オブジェクトタイプのすべてのプロパティをオプションにします

```ts
interface Todo {
  title: string;
  description: string;
}
const todo1: Todo = { 
  title: "foo", // descriptionがないためエラー
};
const todo2: Partial<Todo> = {
  title: "foo", // ok
};
```

### Required型

オブジェクトタイプのすべてのプロパティを必須になります

```ts
interface Todo {
  title?: string;
  description?: string;
}
const todo1: Todo = { 
  title: "foo", // ok
};
const todo2: Required<Todo> = {
  title: "foo", // descriptionがないためエラー
};
```

### Readonly型

オブジェクトタイプのすべてのプロパティを読み取り専用にします

```ts
interface Todo {
  title: string;
  description: string;
}
const todo: Readonly<Todo> = {
  title: "foo",
  description: "bar",
};
todo.title = "fooo"; // 読み取り専用プロパティであるため、'title' に代入することはできません。
```

### Record型

`Record<K,T>` キーにK、値にTを持つオブジェクトタイプになります

```ts
interface CatInfo {
  age: number;
  breed: string;
}
type CatName = "miffy" | "boris" | "mordred";
const cats: Record<CatName, CatInfo> = {
  miffy: { age: 10, breed: "Persian" },
  boris: { age: 5, breed: "Maine Coon" },
  mordred: { age: 16, breed: "British Shorthair" },
};
```

### Pick型
`Pick<T,K>` 型Tの中のプロパティKを持つオブジェクトタイプになります

```ts
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};
```

### Omit型

`Omit<T, K>` 型Tの中のプロパティKを除いたオブジェクトタイプになります

```ts
interface Todo {
  title: string;
  description: string;
  completed: boolean;
  createdAt: number;
}
type TodoInfo = Omit<Todo, "completed" | "createdAt">;

const todoInfo: TodoInfo = {
  title: "Pick up kids",
  description: "Kindergarten closes at 5pm",
};
```

### Exclude型

`Exclud<T,K>` 型Tから型Uと関連するプロパティを除いたオブジェクトタイプになります


```ts
type T0 = Exclude<"a" | "b" | "c", "a">; // type T0 = "b" | "c"

type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // type T1 = "c"

type T2 = Exclude<string | number | (() => void), Function>; // type T2 = string | number
```

### Extract型

`Extract<T,K>` 型Tと型Kの両方に共通するプロパティだけを持つオブジェクトタイプになります

```ts
type T0 = Extract<"a" | "b" | "c", "a" | "f">; // type T0 = "a"
     
type T1 = Extract<string | number | (() => void), Function>; // type T1 = () => void
```

### NonNullable型
nullとundefinedを除いたオブジェクトタイプになります

```ts
type T0 = NonNullable<string | number | undefined>; // type T0 = string | number

type T1 = NonNullable<string[] | null | undefined>; // type T1 = string[]
```

### Parameters型
`Parameters<T>` Tに関数型を受け取り、受け取った型をタプル型として持つオブジェクトタイプになります。
`declare`はParameters型とは関係ないですが、アンビエント宣言といって関数の型定義を明示的に表したものです。

```ts
declare function f1(arg: { a: number; b: string }): void;

type T0 = Parameters<() => string>; // type T0 = []

type T1 = Parameters<(s: string) => void>; // type T1 = [s: string]

type T3 = Parameters<typeof f1>; // type T3 = [arg: { a: number; b: string; }]
```

### ConstructorParameters型
`ConstructorParameters<T>` Tにコンストラクタの引数の型をタプル型として持つオブジェクトタイプになります。 Parametersのコンストラクタ版です。

```ts
class Foo {
  constructor(arg1: string, arg2?: boolean) {}
}

type Bar = ConstructorParameters<typeof Foo>; // [string, boolean] | [string]

```

### ReturnType型
`ReturnType<T>` Tで受け取った関数型の戻り型を持つオブジェクトタイプになります

```ts
declare function f1(): { a: number; b: string };

type T0 = ReturnType<() => string>; // type T0 = string

type T1 = ReturnType<(s: string) => void>; // type T1 = void

type T4 = ReturnType<typeof f1>; // type T4 = { a: number; b: string; }
```

### InstanceType型
`InstanceType<T>` Tで受け取ったコンストラクター関数のインスタンス型を持つオブジェクトタイプになります

```ts
class C {
  x = 0;
  y = 0;
}

type T = InstanceType<typeof C>; // type T = C
```

### ThisParameterType型
`Parameters型`では`this`を受け取れないのに対し、`ThisParameterType型`では受け取った関数型の`this`パラメーターの型を持つオブジェクトタイプになります

```ts
function fn(this: { a: string }, b: number): string {
  return this.a;
}
type T0 = typeof fn;

type T1 = Parameters<T0>; // type Param = [b: number]

type T2 = ThisParameterType<T0>; // type T2 = { a: string; }
```

### OmitThisParameter型
受け取った関数型の`this`パラメーターの型を除いた残りの型を持つオブジェクトタイプになります

```ts
declare function fn(this: { a: string; b: boolean }, c: number): void;
type T0 = typeof fn;

type T1 = OmitThisParameter<T0>; // type T1 = (c: number) => void
```

### ThisType型
`ThisType<T>` Tのプロパティをthisで扱えるようにします

```ts
type ObjectDescriptor<D, M> = {
    data?: D;
    methods?: M & ThisType<D & M>; // methodsのthisの型がD&Mになります
  };
  
  function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
    let data: object = desc.data || {};
    let methods: object = desc.methods || {};
    return { ...data, ...methods } as D & M;
  }
  
let obj = makeObject({
  data: { x: 0, y: 0 },
  methods: {
    moveBy(dx: number, dy: number) {
      this.x += dx; // D(dataのプロパティ)がthisで利用できる
      this.y += dy;
    },
    double(dx, dy) {
      this.moveBy(dx, dy); // M(methodsのプロパティ)がthisで利用できる
      this.moveBy(dx, dy);
    },
  },
});

obj.double(5, 5);

```

### Uppercase型
文字列内の各文字を大文字に変換します


```ts
type Greeting = "Hello, world"
type ShoutyGreeting = Uppercase<Greeting> // type ShoutyGreeting = "HELLO, WORLD"

type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`
type MainID = ASCIICacheKey<"my_app"> // type MainID = "ID-MY_APP"
```

### Lowercase型
文字列内の各文字を同等の小文字に変換します

```ts
type Greeting = "Hello, world"
type QuietGreeting = Lowercase<Greeting> // type QuietGreeting = "hello, world"

type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}`
type MainID = ASCIICacheKey<"MY_APP"> // type MainID = "id-my_app"
```

### Capitalize型
文字列の最初の文字を大文字に変換します

```ts
type LowercaseGreeting = "hello, world";
type Greeting = Capitalize<LowercaseGreeting>; // type Greeting = "Hello, world"
```

### Uncapitalize型
文字列の最初の文字を小文字に変換します

```ts
type UppercaseGreeting = "HELLO WORLD";
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>; // type UncomfortableGreeting = "hELLO WORLD"
```

### おわりに
今回勉強してきた以外にもTypeScriptにはたくさんの機能や特徴があります。TypeSctipt以外にも学ばないといけないことはたくさんあり、日々更新される変更や追加機能に私自身全くついていけていないですが、焦らず、学びに楽しみを見出しながら続けていきたいと思います。
最後まで読んでいただきありがとうございました。

***

### 関連記事
[TypeScript①（基礎)](https://qiita.com/zgoovw_/items/7a6fcb18b92c6073f120)
[TypeScript②（型の機能）](https://qiita.com/zgoovw_/items/c44d28f3cd2fdf91219f)