TypeScriptのジェネリクス（Generics）をふわっと理解する

# はじめに
ライブラリなどで雰囲気で使っていたジェネリクスですが、しっかりと理解しようと言うことで備忘録も兼ねて今回記事にすることにしました。主にTypeScript公式ドキュメント[[1]]を参考に学習していきます。コードも基本的に[[1]]から引用しています。
# ジェネリクスってなに？
型引数を利用して、実際に利用されるまで型が確定しないクラスや関数などを実現するためのもの。TypeScript特有のものではなく、他の言語にもあります。

例として以下のような関数を考えます。この関数では引数は`number`型しか渡すことは出来ません。
```typescript
function identity(arg: number): number {
  return arg;
}
```
[[1]]より引用

次にジェネリクスを使用した以下のような関数を考えます。この関数では型を引数としてとるため、`number`型以外でも引数として渡すことが出来ます。

```typescript
function identity<Type>(arg: Type): Type {
  return arg;
}
```
[[1]]より引用

このようにジェネリクスを用いることで、ロジックは共通しているが異なる型を引数とするものを１つにすることが出来ます。今回は様々なジェネリクスの書き方を学んでいこうと思います。また、今回の例では違いますが、ジェネリクスの型引数名は一般的にT,Uなどが使われることが多いです。

# Extends
`extends`は型に制約をつけることがつけることが可能です。

```typescript
type User = {
  name: string;
  age: number;
};

function f<T extends User>(arg: T): string {
  return arg.name;
}

```
上の例では`User`型しか受け付けないようになっています。


# Utility Types
型から別の型を出してくれる型であり、型の世界の関数というイメージです。種類は少なくないですが、覚えられなくもない数です。下のコードは`Readonly`を用いた使用例です。

```typescript
type Person = {
  surname: string;
  middleName?: string;
  givenName: string;
};
type ReadonlyPerson = Readonly<Person>;
```

# Keyof Type Operator
オブジェクトタイプを受け取り、そのキーの文字列または数値リテラル結合を生成。

```typescript
type Point = { x: number; y: number };
//P1とP2は同じ型
type P1 = keyof Point;
type P2 = "x" | "y";
```

# Typeof Type Operator
変数またはプロパティの型を参照。基本的には他の型演算子と組み合わせて使用。

```typescript
let s = "hello";
// let n: string と同じ
let n: typeof s;
```

```typescript
function f() {return { x: 10, y: 3 };}
//関数が返すのは値なので値の型を参照するためにtypeofを使用
type P = ReturnType<typeof f>;
```

# Indexed Access Types
プロパティや配列要素の型を参照する方法を提供。
下のコード例ではオブジェクトタイプに対して各キーのtypeを取り出しています。

```typescript
type Person = { age: number; name: string; alive: boolean };
// type I1 = string | number;
type I1 = Person["age" | "name"];
// type I2 = string | number | boolean;
type I2 = Person[keyof Person];
```

下のコードは配列に対してnumberでアクセスすることでオブジェクトタイプの方を返しています。多次元配列のような書き方でオブジェクトタイプ内のキーの型を入手することも可能です。

```typescript
const MyArray = [
  { name: "Alice", age: 15 },
  { name: "Bob", age: 23 },
];
// type Person = {name: string;age: number;};
type Person = (typeof MyArray)[number];
// type Age = number;
type Age = (typeof MyArray)[number]["age"];
```
# Conditional Types
型世界の条件分岐。inferキーワードを用いて型推論することが可能、

下のコードのように参考演算子っぽく振る舞う。
```typescript
type IsNumber<T> = T extends number ? true : false;
type T1 = IsNumber<1>;       //type T1 = true
type T2 = IsNumber<"test">;  //type T2 = false
```

配列の中身の型を型推論で推測する。
```typescript
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
```

# Mapped Types
型のfor文的なもの。オブジェクトのキーを文字列リテラルから取ってくる使い方が頻出。

```typescript
type Language = "jp" | "en";
type T = {
  [key in Language]: string;
};

```

# Template Literal Types
型の世界のテンプレートリテラル。

```typescript
type World = "world";
 
type Greeting = `hello ${World}`;

type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
 
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
```
ただ感情的にこれを型でつかうの気持ち悪く感じてしまう。
 
まだ調べきれてない型の使い方がたくさんあるので調べたら追記します。

# 参考文献
[1]:https://www.typescriptlang.org/docs/handbook/2/generics.html
[2]:https://typescriptbook.jp/reference/generics/type-parameter-constraint
[3]:https://zenn.dev/azukiazusa/scraps/7f3396b6e6cfab
[4]:https://qiita.com/suema0331/items/ed4a80d07edfaa48eb85


[[1]]:Generics
[[2]]:サバイバルtypeScript
[[3]]:type-challenges をやる
[[4]]:【TypeScript】TypeScript の Generics 制約