[Typescript] Generics


#Generics
型を引数として受け取ることができる仕組みです。

↓関数copyを実行するときに、型を引数として渡してあげることができます。
↓関数名と値の間に＜T＞と入れることで、Tが、今後どの場面でも使える型になります。
↓例えば値にも、戻り値にもTが使えますし、let user: T;のようにも使えます。

```.ts
function copy<T>(value: T): T {
  let user: T;
  return value;
}
console.log(copy<string>('hello'));
```

じゃあこのTに入る型はどこで渡すかというと、console.log(copy<string>('hello'));のように、
copyと引数の間に＜string＞として渡してあげます。
そうすると、＜T＞はstring型になります。

↓このTはパラメーターのように何個も渡せます。
↓T以降はU, Vのように書かれることが多いですが、名前はTやUでなく、何でも構いません。

```.ts
function copy<T, U, V>(value: T): T {
  let user: V;
  return value;
}
console.log(copy<string, number, boolean>('hello'));
```

↓引数がオブジェクトの場合、
↓型アサーションではこのように{ name: string }と付ける必要がありましたが、

```.ts
function copy<T>(value: T): T {
  let user: T;
  return value;
}
console.log(copy<{ name: string }>({ name: 'Quill'}));
```

↓genericsでは型推論してくれます。

```.ts
function copy<T>(value: T): T {
  let user: T;
  return value;
}
console.log(copy({ name: 'Quill'}));
```

↓関数でT, Uと渡していてUについて何も書いていないときは、
↓Uはデフォルトでunknown型になっています。

```ts
function copy<T, U>(value: T): T {
  let user: T;
  return value;
}
console.log(copy({ name: 'Quill'}));
```

#型パラメーターに制約を付ける

↓extendsを使います。
↓「オブジェクトかつnameを含んでいて、string型だ」という制約はこう書きます。

```ts
function copy<T extends { name: string }>(value: T): T {
  let user: T;
  return value;
}
console.log(copy({ name: 'Quill'}));
```

#オブジェクトのキーのunion型を作る

↓Kは、type K = "number" | "name"というunion型になっています。
↓このように、オブジェクトのキーを取ってunion型にするものをkeyof演算子と言います。

```.ts
type K = keyof { name: {}; number: number}
```

↓例えば、Uの型をTに依存させたいといったときに、
↓U extends keyof Tのようにすると、Uの型は、name | ageのunion型になります。
↓なので、Uの型はnameかageに限定されますし、関数の中でvalue[key];のように使えます。

```ts
function copy<T extends { name: string }, U extends keyof T>(value: T, key: U): T {
  value[key];
  return value;
}
console.log(copy({ name: 'Quill', age: 20}, 'age'));
```

#classに対してgenericsを使う方法

↓定義するclass名の後にgenericsを追加します。
↓生成するときはインスタンス化するclass名（今回だったらnew）の後に追加します。

```ts
class LightDataBase<T extends string | number | boolean> {
}

const stringLightDataBase = new LightDataBase<string>();
```

#Interfaceに対してgenericsを使う方法
↓interfaceを定義した名前の後に、＜T＞のように付けることができます。

```ts
interface TmpDataBase<T> {
  id: number;
  data: T[];
}
```

↓じゃあTの型はどこで定義するかというと、ここです。

```ts
const tmpDataBase: TmpDataBase<number> = {
  id: 3,
  data: [20]
}
```

#Utility Types
utility　＝　実用性

型のlibraryです。
Typescriptに内蔵されたジェネリック型になります。

↓例えばPartial型というものがあります。これは、

```ts
interface ToDo {
  title: string;
  text: string;
}

type ToDoable = Partial<ToDo>
```
↓全部optional型になるUtility classです。

```ts
type ToDoable = {
    title?: string;
    text?: string;
}
```

↓Readonly classだったら、全てにreadonlyが付きます。

```ts
type ReadTodo = Readonly<ToDo>

type ReadTodo = {
    readonly title: string;
    readonly text: string;
}
```

#Genericsの型のParameterにデフォルトの型を使う
↓Genericsの型のParameterは、TとかUのことです。
↓T = anyのようにすると使えるようになります。

```ts
interface ResponseData<T = any> {
  data: T;
  status: number;
}

let tmp: ResponseData;
```

#Mapped Types
型のfor文です。反復させるような処理を行えます。

↓for文と同じくinを使います。

```ts
type MappedTypes = {
  [P in 'tomato' | 'pumpkin']: P;
}
```

↓MappedTypesはiterationしています。

```ts
type MappedTypes = {
    tomato: "tomato";
    pumpkin: "pumpkin";
}
```

↓keyofと併用することもあります。
↓MappedTypesは先ほどと同じ内容です。

```ts
interface Vegetables {
  tomato: string;
  pumpkin: string;
}
type MappedTypes = {
  [P in keyof Vegetables ]: P;
}
```

↓genericsを用いて、MappedTypesを色々なところで使えるようにする事もよくあります。

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

↓また、MappedTypesは先頭にreadonly, 末尾に?を付けることもできます。

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

----------------------------------------------------
//結果

type MappedTypes<T> = {
    readonly tomato?: "tomato";
    readonly pumpkin?: "pumpkin";
}
```

↓readonlyの頭に ー を付けると、Vegetablesのreadonlyが全て打ち消されます。
↓?の頭に ー を付けると、Vegetablesのoptional型が全て打ち消されます。

```ts
type MappedTypes <T> = {
  -readonly [P in keyof Vegetables ]-?: P;
}
```

#Conditional Types
型のif文みたいなものです。

↓tomato型が、string型に代入できるのであれば、number、できないのであればboolean
↓といった意味になります。

```ts
type ConditionalTypes = 'tomato' extends string ? number : boolean;
```

↓extendsを基準とし、左が右に代入できるような方向性のとき、Rが使えるという意味です。
↓infer Rはanyのようなもので、何でもOKです。
↓の上で、{ tomato: 'tomato' }から{ tomato: infer R }の型を推論します。
↓infer Rが、{ tomato: 'tomato' }に最も近くなるような型になります。

```ts
type ConditionalTypesInfer = { tomato: 'tomato' } extends { tomato: infer R } ? R : boolean;
```

よって今回は、ConditionalTypesInferが"tomato"型になります。

**DistributiveConditionalTypes**

↓DistributiveConditionalTypesというものがあって、
↓extendsの左側がunion型になっているとき、どうなるの？という意味を表現します。
↓結論、分配されます。

```ts
type DistributiveConditionalTypes<T> = T extends 'tomato' ? number : boolean;
let tmp: DistributiveConditionalTypes<'tomato' | 'pumpkin'>
```
つまり、number | booleanのunion型になります。
また、DistributiveConditionalTypesを使うときは、genericsを用います。