この記事を読むのにオススメな対象者
- TS触り始めてまもない人
- ジェネリクスを使ったことがない人
- フロントエンドに興味を持ち始めた人
Genericsとは
ジェネリクスとは型の決定を遅延させ、型をより柔軟に使いこなすシステムです。
ジェネリクスに割り当てた型定義に対し、< >
の中に型を指定することで型を利用する時に、可変にできる便利な機能です。
TS開発していく上で、知っていると型を共通化したり何かと便利なので
そんな機能もあるんだという気持ちで見ていってください!
基本的な書き方
ジェネリクスを利用する場合には、型の名称に続いて括弧< >
の中にT
などの型エイリアスを指定して利用します。
interface Text<T> {
value: T
}
使い方
先ほど作成した、型エイリアスのに利用したい型を指定します。
今回の場合、string
で指定しています。
const text: Text<string> = { value: 'hoge' }
エラーになる使い方
先ほど作成した型には、ジェネリクスが必須のため、省略した記述をするとエラーになる。
また、ジェネリクスで定義している型と値の型が一致しない場合にも同様エラーの扱いになります。
// Error!!: ジェネリクスが指定されていない
const text: Text = { value: 'hoge' }
// Error!!: numbeer型にstring型の値は入れられません。
const text: Text<number> = { value: "fuga" }
ジェネリクスに初期設定の値を定義する方法
ジェネリクスにも初期値が設定できるため、初期の型を設定したい場合には
<T = string>
のように定義すると利用側でジェネリクスを指定しなくても利用できるようになります。
interface Text<T = string> {
value: T
}
初期値を指定していると、ジェネリクスを省略して記述しても
初期値に入れた型として処理されるため、エラーにならない。
// ジェネリクスがstringとして処理されるためエラーにならない。
const text: Text = { value: 'hoge' }
// Error!!: 初期値のstring型で定義されるため、number型の値を入れるとエラーになる。
const text: Text = { value: 100 }
また、ジェネリクスの何でも利用側で型定義できてしまうのを制御したい要望もあると思います。その場合にはextends
シグネチャを利用する事で型に制約を加える事も可能です。
interface Text<T extends string | number> {
value: T
}
const text: Text<string> = { value: "huge" }
const text: Text<number> = { value: 100 }
// Error!!: string型または、number型にboolean型を入れられません。
const text: Text<boolean> = { value: false }
もちろん型制約しながら初期値を定義する事も可能です。
// 初期のジェネリクス型をstringとしたい場合
interface Text<T extends string | number = string> {
value: T
}
// 初期のジェネリクス型をnumberとしたい場合
interface Text<T extends string | number = number> {
value: T
}
ジェネリクスを上手に利用すると、一部の型では共通化する事ができ整理する事もでき結果的に保守のしゃすいコードが書きやすくなります。
関数でのジェネリクスの使い方
function handleTextChange<T>(text: T) {
return { value: text };
}
const strText = handleTextChange('hoge');
const numText = handleTextChange(100);
// このように推論される。
strText: { value: string };
numText: { value: number };
// アロー関数での書き方
const handleTextChange = <T>(text: T) => {
return { value: text };
}
また、関数の場合だとジェネリクスを使っていても、利用する際に型指定は必須じゃないため省略して記載してもエラーになる事はなく型推論されます。
function handleTextChange<T extends string | number>(text: T){
return { value: text }
}
handleTextChange('hoge');
handleTextChange(100);
// string | numberにbooleanを入れようとしたのでエラーになる。
handleTextChange(false);
また、関数でもextends
を利用して型制約を入れる事が可能なため
何でも型を指定して欲しくないケースなどでは、extends
をうまく利用して制御することが可能です。
最後に
ジェネリクスは便利な機能です。使いこなせるようになると
コードを大幅に縮小できたり共通化できたり、保守しやすいコードが
書けるようになり、TSを書くのが楽しくなります。ぜひTSを普段触っていてジェネリクスをまだ触ってない人がいれば一度、試しにプロジェクトで利用してみてください!