2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【TypeScript】ジェネリクスの仕組み 🤔

Last updated at Posted at 2022-12-05

ジェネリクスとは

型を引数として受け取ることができる仕組み。
型を引数に取れることで、型だけが異なる同じ関数の複製などを避けられる👏🏻

ジェネリクスを用いると、型の安全性とコードの共通化を両立することができます。

サバイバルTypeScript:ジェネリクス

下記の例では、取れる引数の値をnumber型にしている。
しかし、型を指定したままではstringやオブジェクトなど他の型を取りたい場合に利用できず、型が異なるだけの関数を作成しなければならない。

function copy(value: number): number {
  return value;
}
console.log(copy(11));

ジェネリクスを利用し、型を引数に取ることで、再利用性を高めることができる。
下記の例のように<T>と記述することで、そこに型の実引数が渡ってくる。
<T>部分は自由に記載できるが、慣習的にはTypeという意味の<T>にするよう。

function copy<T>(value: T): T {
  return value;
}
console.log(copy<string>("hello"));

複数取ることもできる

function copy<T, T1, T2>(value: T): T {
  return value;
}

copy<string>("hello")の型の実引数部分を省略できる。
省略しても、関数の実引数から型推論される。
下記の例では型の実引数を省略しているが、.で繋ぐと、引数で渡しているhelloから型推論され、stringが持つメソッドが補完で出てきていることがわかる。
スクリーンショット 2022-12-03 14.50.47.png

型パラメータ(引数)に制約をつける方法

extendsキーワードを使うと制約をつけられる。
下記の例では、{ name: string }とextendsしている。

function copy<T extends { name: string }>(value: T): T {
  return value;
}
console.log(copy({ name: "Quill" }));

プロパティ名をextendsの内容と異なるものにするとエラー
スクリーンショット 2022-12-03 18.00.40.png

keyof演算子

keyof オブジェクトの型とすることで、オブジェクトの型のキーを取り出して、返す。

keyofはオブジェクト型からプロパティ名を型として返す型演算子です。

サバイバルTypeScript:keyof演算子

type K = keyof { name: string; age: number };

type Kはnameとageのユニオン型になっていることがわかる。
スクリーンショット 2022-12-04 18.18.02.png

Classに対してジェネリクスを使用する方法

classに対してもジェネリクスは使用できる。
下記は使用例。

class LightDatabase<T extends string | number | boolean> {
  private data: T[] = [];
  add(item: T) {
    this.data.push(item);
  }
  remove(item: T) {
    this.data.splice(this.data.indexOf(item), 1);
  }
  get() {
    return this.data;
  }
}
const stringLightDatabase = new LightDatabase<string>();
stringLightDatabase.add("Apple");
stringLightDatabase.add("Banana");
stringLightDatabase.add("Grape");
stringLightDatabase.remove("Banana");
console.log(stringLightDatabase.get()); // → [ 'Apple', 'Grape' ]

interfaceに対してジェネリクスを使用する方法

interface TmpDatabase<T> {
  id: number;
  data: T[];
}
const tmpDatabese: TmpDatabase<number> = {
  id: 3,
  data: [32],
};

ユーティリティ型(utility type)の紹介

ユーティリティ型とは、型のライブラリ。

ユーティリティ型(utility type)は、型から別の型を導き出してくれる型です。functionが実行時の世界の関数だとしたら、ユーティリティ型は型の世界の関数といったイメージです。

サバイバルTypeScript:ユーティリティ型

上記サバイバルTypeScriptで代表的なユーティリティ型を掲載してくれているのが、そのうちの一つであるPartialというユーティリティ型を使用した例。

Partialは、オブジェクト型Tのすべてのプロパティをオプションプロパティにするユーティリティ型です。

サバイバルTypeScript:Partial

interface Todo {
  title: string;
  text: string;
}
type Todoable = Partial<Todo>

全てのプロパティがオプションになっていることがわかる。
スクリーンショット 2022-12-05 22.03.53.png

Mapped Typesを使用する方法

Mapped Typesは型のfor文である。

例1)string型で展開

type MappedTypes = {
  [P in 'tomato' | 'pumpkin']: string
}

スクリーンショット 2022-12-05 22.19.42.png

例2)文字列リテラルで展開

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

スクリーンショット 2022-12-05 22.18.34.png

先頭にreadonlyをつけると全てのプロパティがreadonlyになる。

type MappedTypes = {
  readonly [P in 'tomato' | 'pumpkin']: string
}

スクリーンショット 2022-12-05 22.24.20.png

?をつければオプショナルになる。

type MappedTypes = {
  readonly [P in 'tomato' | 'pumpkin']?: string
}

スクリーンショット 2022-12-05 22.25.45.png

Conditional Types

Conditional Typesとは型のif文。
extendsの左側が右側に代入できるかどうかの条件分。

tomatostringに代入可能だったら、number型、そうでなければboolean型を返す。

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

スクリーンショット 2022-12-05 22.35.49.png

2
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?