Writer: tokuyasu 更新日:2024/05/16
こんにちは、デジナーレ福岡オフィスの徳安です。
今回はTypeScriptのジェネリクスについてまとめてみました。
Generics
ジェネリクスとは、「型も変数のように、扱えるようにすること」
ジェネリクスを用いると、「型の安全性とコードの共通化」を両立することができる。
コードを共通化する際に、共通化したいすべてのコードが同じ型になるわけではない。
any型を使用し共通化することで、コードの共通化はできるが、型の安全性が欠ける。
逆に、型の安全性を優先し、共通化せずに実装すると、
同じようなコードをたくさん作成する必要が出てきてしまう。
このような問題を解決するために、使用するものがGenerics(ジェネリクス)。
Generics の使い方
以下のように同じようなコードだが、返したい結果の「型が異なる場合」、
同じような記述を繰り返さないといけない。
Genericsを使用しない場合
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// number型 const test = (arg: number): number => { return arg; }; // string型 const test2 = (arg: string): string => { return arg; }; test(1); //=> 1 test2("文字列"); //=> 文字列 ``` |
上記のコードで異なる部分は、「TypeScriptの型のみ」。
この型が変数のように扱えれば、共通化できる。
1 2 3 4 5 6 7 |
// 注意: これは架空の文法です const test = <type>(arg: <type>): <type> => { return arg; }; test<number>(1); test<string>("文字列"); |
このように、関数を実行する際に<type>で、記述した部分の型を
変数として扱うことができれば、共通化できる。
Genericsを使用する場合
以下のように、関数の引数の()の前に Generics を記述する。
1 2 3 4 5 6 |
const test = <T>(arg: T): T => { return arg; }; test<number>(1); test<string>("文字列"); |
Generics は「暗黙的に型が解決される仕組み」があるため、
引数で型を指定しなくても、TypeScriptが暗黙的に型を定義してくれる。
1 2 3 4 5 6 |
const test = <T>(arg: T): T => { return arg; }; test(1); // test: <1>(arg: 1) => 1 test("文字列"); // test: <"文字列">(arg: "文字列") => "文字列" |
extends による型制約
extends を使用し、ジェネリクスの型Tを特定の型に限定することができる。
1 2 3 4 5 6 |
const stringFunc = <T extends string>(arg: T) => { return { value: arg }; }; stringFunc<string>(""); stringFunc(1); // 型 'number' の引数を型 'string' のパラメーターに割り当てることはできません。 |
ジェネリクスの場合extendsを使用せずに実装も可能だが、
どんな型でも渡せてしまうため、型安全や、保守性の向上のために
どのような型が来るのかわかっている場合は、extendsで型規制を行う。
ここまで読み進めていただきありがとうございます。