TypeScriptのジェネリクスをふんわりとしか理解していなかったので改めて調べてみた
型を変数のように扱える仕組み
一つの定義でさまざまな型に対応ができる
Generic→「汎用的な」みたいな意味、Genericsはその複数形
書き方は以下のように記述
// <型名>
<T> // 一般的にはT(Type)と記載するが、一応任意でかけるなんで必要か?
コードの型チェックを行いつつ、再利用性が高まるから
と自分は考えている
本来TypeScriptでは以下のようなジレンマがあると思う
- 汎用的にしようとすると型の厳密性が失われる
- 厳密にしすぎても、似たような関数が出来上がり、冗長な記述になってしまう
これを解決してくれるのがジェネリクス!!!
// 普通の関数: 値を受け取る
function wrap(value: string) {
return { data: value };
}
wrap("hello") // → { data: "hello" }
// ジェネリクス: 型を受け取る
function wrap<T>(value: T) {
return { data: value };
}
wrap<string>("hello") // → { data: "hello" }(dataはstring型)
wrap<number>(42) // → { data: 42 }(dataはnumber型)また、複数の型パラメータも使用可能
※慣習的にTの次はU、Vなどのアルファベット順になるみたい
※特定の用途に応じてKeyの頭文字のK、Valueの頭文字のVを使用することもある
const test = <T, U>(first: T, second: U): [T, U] => {
return [first, second];
}
console.log(test('test', 111));
console.log(test(true, 'テストします'));
他にも型制約を行なって特定のプロパティに安全にアクセスするような書き方もできる
※keyof Tの部分をKにextendsしており、KはtestUserのキーに当たる部分が推論される
const getProperty = <T, K extends keyof T> (obj: T, key: K): T[K] => {
return obj[key];
};
const testUser = {
name: 'test',
age: 20,
body: 'aa'
}
console.log(getProperty(testUser, 'name'));
console.log(getProperty(testUser, 'age'));
console.log(getProperty(testUser, 'body'));
// console.log(getProperty(testUser, 'test')); // これはKeyに含まれていないので×まとめ
あまりちゃんと学んでこなかったため、理解に時間がかかった。
型も引数のように扱うという発想のジェネリクスだが、慣れると色々使いまわせて便利だろうな〜と感じた。
ReactであろうがVueであろうが、今後も型安全に開発していきたい...(雰囲気でTypeScriptを使わないようにしたい)
