ジェネリクスとは
型を引数として受け取ることができる仕組み。
型を引数に取れることで、型だけが異なる同じ関数の複製などを避けられる👏🏻
ジェネリクスを用いると、型の安全性とコードの共通化を両立することができます。
下記の例では、取れる引数の値を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が持つメソッドが補完で出てきていることがわかる。
型パラメータ(引数)に制約をつける方法
extends
キーワードを使うと制約をつけられる。
下記の例では、{ name: string }
とextendsしている。
function copy<T extends { name: string }>(value: T): T {
return value;
}
console.log(copy({ name: "Quill" }));
プロパティ名をextends
の内容と異なるものにするとエラー
keyof演算子
keyof オブジェクトの型
とすることで、オブジェクトの型のキーを取り出して、返す。
keyofはオブジェクト型からプロパティ名を型として返す型演算子です。
type K = keyof { name: string; age: number };
type K
はnameとageのユニオン型になっていることがわかる。
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で代表的なユーティリティ型を掲載してくれているのが、そのうちの一つであるPartial
というユーティリティ型を使用した例。
Partialは、オブジェクト型Tのすべてのプロパティをオプションプロパティにするユーティリティ型です。
interface Todo {
title: string;
text: string;
}
type Todoable = Partial<Todo>
Mapped Typesを使用する方法
Mapped Typesは型のfor文である。
例1)string型で展開
type MappedTypes = {
[P in 'tomato' | 'pumpkin']: string
}
例2)文字列リテラルで展開
type MappedTypes = {
[P in 'tomato' | 'pumpkin']: P
}
先頭にreadonly
をつけると全てのプロパティがreadonlyになる。
type MappedTypes = {
readonly [P in 'tomato' | 'pumpkin']: string
}
?
をつければオプショナルになる。
type MappedTypes = {
readonly [P in 'tomato' | 'pumpkin']?: string
}
Conditional Types
Conditional Typesとは型のif文。
extends
の左側が右側に代入できるかどうかの条件分。
tomato
はstring
に代入可能だったら、number型
、そうでなければboolean型
を返す。
type ConditionalTypes = 'tomato' extends string ? number : boolean;