- Day 22:Genericsで「型に強い汎用関数」を手に入れる
- Genericsとは:型に対する「変数」を持つイメージ
- Generic Functionの基本形を押さえる
- Step 1:文字列・数値・Objectを処理できる汎用関数を考える
- Step 2:ジェネリック関数として定義する
- Step 3:文字列・数値・Objectで実際に使ってみる
- Step 4:型推論を使って、もっとシンプルに書いてみる
- Step 5:もう少し実用的な処理を足してみる
- Day 22 練習テンプレート:汎用ログ関数+少し応用
- Day 22のまとめ
- Day 22:Generics演習で「どんな配列でも扱える汎用関数」を作る
- Genericsの基本イメージをもう一度整理する
- Step 1:getFirst<T>() を作る
- Step 2:getLast<T>() を作る
- Step 3:reverse<T>() を作る
- Step 4:findById<T>() を作る
- Day 22 練習テンプレート:4つのジェネリック関数をまとめて試す
- Day 22 演習のまとめ
Day 22:Genericsで「型に強い汎用関数」を手に入れる
第4週に入り、いよいよTypeScriptの“応用編”に入っていきます。 Day 22のテーマは Generics(ジェネリクス)。 ここでは、「型を引数として扱う」という少し不思議だけれど、とても強力な仕組みを体験していきます。
到達イメージはこうです。
文字列・数値・オブジェクトなど、いろいろな型を 「安全に」「使い回せる」汎用関数で扱えるようになる。
Genericsとは:型に対する「変数」を持つイメージ
<T> は「型のためのパラメータ」です
Genericsをひと言でいうと、
「型を後から差し込めるようにする仕組み」
です。
そのときに使うのが <T> という記法です。
function identity<T>(value: T): T {
return value;
}
TypeScriptここでのポイントは、
Tは「型の名前」value: Tは「valueはT型ですよ」: T(戻り値)も「T型を返しますよ」
という意味になっていることです。
T は「型のための変数」のようなもので、 関数を呼び出すときに「このTにはstringを入れよう」「numberを入れよう」と決めて使います。
Generic Functionの基本形を押さえる
まずはシンプルな例から
先ほどの identity 関数を、もう少し丁寧に見てみます。
// ジェネリック関数の基本形
function identity<T>(value: T): T {
// valueの型はT
return value; // T型の値をそのまま返す
}
// 呼び出し例
const s = identity<string>("hello"); // T = string
const n = identity<number>(123); // T = number
TypeScript重要ポイント:
<string>と書いた瞬間、Tはstringに置き換えられます。<number>と書いた瞬間、Tはnumberに置き換えられます。- 関数の中身は同じでも、「どの型で使うか」を後から決められるのがGenericsの強みです。
型推論で <T> を省略できることも多いです
TypeScriptは賢いので、引数から T を推論してくれます。
const s2 = identity("hello"); // Tはstringと推論される
const n2 = identity(123); // Tはnumberと推論される
TypeScriptGenericsは「型を引数として扱う」仕組みですが、 毎回 <T> を書かないといけないわけではなく、 多くの場合は「TypeScriptがいい感じに推論してくれる」と思っておいて大丈夫です。
Step 1:文字列・数値・Objectを処理できる汎用関数を考える
ここから、実践テーマに入っていきます。
文字列・数値・Objectを処理できる汎用関数を作る。
まずは、「どんな処理をする関数にするか」を決めましょう。
今回は、次のような関数を目指します。
- 引数にどんな型でも受け取れる(string / number / objectなど)
- 受け取った値を「ログに出しつつ、そのまま返す」
つまり、「型安全なログ付きidentity関数」のようなイメージです。
Step 2:ジェネリック関数として定義する
型パラメータTを使った汎用関数
// どんな型でも受け取ってログを出し、そのまま返す汎用関数
function logAndReturn<T>(value: T): T {
console.log("値:", value); // ここではvalueの型はT
return value; // T型の値をそのまま返す
}
TypeScriptここでのポイントは、
function logAndReturn<T>(value: T): T<T>:この関数は「型パラメータT」を持っていますvalue: T:引数valueはT型です: T:戻り値もT型です
という構造になっていることです。
Step 3:文字列・数値・Objectで実際に使ってみる
文字列を渡す
const strResult = logAndReturn<string>("Hello Generics");
// T = string
// valueの型はstring
// 戻り値の型もstring
TypeScriptTypeScript的には、
valueはstringstrResultもstring
として扱われます。
数値を渡す
const numResult = logAndReturn<number>(42);
// T = number
// valueの型はnumber
// 戻り値の型もnumber
TypeScriptここでも、
valueはnumbernumResultもnumber
として扱われます。
Objectを渡す
type User = {
id: number;
name: string;
};
const user: User = { id: 1, name: "Taro" };
const userResult = logAndReturn<User>(user);
// T = User
// valueの型はUser
// 戻り値の型もUser
TypeScript重要ポイント(ここをしっかり押さえておくと一気に楽になります):
- 関数の中身はまったく同じでも、 呼び出し時に「Tに何を入れるか」を変えることで、 文字列・数値・オブジェクトなど、いろいろな型に対応できます。
- しかも、戻り値の型もちゃんとTになるので、 呼び出し側で「型安全に」その値を扱えます。
Step 4:型推論を使って、もっとシンプルに書いてみる
毎回 <string> や <User> と書くのは少し面倒なので、 TypeScriptの「型推論」に任せてみます。
const strResult2 = logAndReturn("Hello"); // Tはstringと推論される
const numResult2 = logAndReturn(99); // Tはnumberと推論される
const userResult2 = logAndReturn({ id: 2, name: "Hanako" }); // Tは { id: number; name: string } と推論される
TypeScriptポイント:
- Genericsは「型を引数として扱える」仕組みですが、 実際のコードでは「型を明示することもできるし、推論に任せることもできる」柔らかい使い方ができます。
- 初心者の方は、まず「推論に任せるスタイル」で慣れていくのがおすすめです。
Step 5:もう少し実用的な処理を足してみる
ただログを出して返すだけだと味気ないので、 「型に応じて少しだけ違う表示をする」バージョンも考えてみます。
typeof を使って軽く分岐する例
// 型に応じてログの出し方を少し変えるジェネリック関数
function describeAndReturn<T>(value: T): T {
const typeOfValue = typeof value;
if (typeOfValue === "string") {
console.log("文字列です:", value);
} else if (typeOfValue === "number") {
console.log("数値です:", value);
} else {
console.log("オブジェクトなどの値です:", value);
}
return value;
}
// 使用例
describeAndReturn("Hello"); // 「文字列です: Hello」
describeAndReturn(123); // 「数値です: 123」
describeAndReturn({ id: 1, name: "Taro" }); // 「オブジェクトなどの値です: { ... }」
TypeScript注意ポイント:
- 関数の型としては
Tを使っていますが、 実行時にはtypeofで「実際の中身」を見て分岐しています。 - Genericsは「コンパイル時の型の話」ですが、 実行時の分岐と組み合わせることで、より柔軟な処理が書けます。
Day 22 練習テンプレート:汎用ログ関数+少し応用
最後に、今日の内容をひとつのファイルにまとめた練習テンプレートを載せます。 このままコピーして、型を増やしたり、処理内容を変えたりしながら、 「Genericsで型に強い汎用関数を書く」感覚を育ててみてください。
// day22-generics-basic.ts
// Day 22:Generics入門「汎用関数を作る」
// 1. シンプルなジェネリック関数
function logAndReturn<T>(value: T): T {
console.log("値:", value);
return value;
}
// 使用例
const strResult = logAndReturn("Hello Generics"); // Tはstring
const numResult = logAndReturn(42); // Tはnumber
const objResult = logAndReturn({ id: 1, name: "Taro" }); // Tは { id: number; name: string }
// 2. 型に応じてログの出し方を変えるジェネリック関数
function describeAndReturn<T>(value: T): T {
const typeOfValue = typeof value;
if (typeOfValue === "string") {
console.log("文字列です:", value);
} else if (typeOfValue === "number") {
console.log("数値です:", value);
} else {
console.log("オブジェクトなどの値です:", value);
}
return value;
}
// 使用例
describeAndReturn("Hello");
describeAndReturn(123);
describeAndReturn({ id: 2, name: "Hanako" });
TypeScriptDay 22のまとめ
Day 22では、
- Genericsとは何か(「型のためのパラメータ」を持つ仕組み)
<T>を使ったGeneric Functionの基本形- 文字列・数値・オブジェクトを、ひとつのジェネリック関数で安全に扱う流れ
- 型推論を活かして、呼び出し側をシンプルに書くスタイル
- 実行時の分岐(
typeof)と組み合わせて、少しだけ賢い汎用関数にするアイデア
を、具体的なコードとコメント付きで体験しました。
Genericsは、最初は少し抽象的に感じるかもしれませんが、 「型を後から差し込める」「同じロジックをいろいろな型で使い回せる」という感覚が掴めてくると、 一気にTypeScriptの世界が広がっていきます。
Day 22:Generics演習で「どんな配列でも扱える汎用関数」を作る
第4週のテーマであるGenerics(ジェネリクス)を、もう一歩踏み込んで練習していきます。 今回の演習では、次の4つの汎用関数を作ることが目標になります。
getFirst<T>()getLast<T>()reverse<T>()findById<T>()
キーワードは変わらずこちらです。
- Genericsとは
<T>- Generic Function
- 型を引数として扱う
ここでは、「配列」を題材にして、 どんな型の配列でも安全に扱える関数を、ステップバイステップで組み立てていきます。
Genericsの基本イメージをもう一度整理する
「型のための変数」としての <T>
Genericsは、
「型を後から差し込めるようにする仕組み」
でした。
<T> は、そのための「型パラメータ」です。
function identity<T>(value: T): T {
return value;
}
TypeScriptTは「型の名前」value: Tは「valueはT型ですよ」: Tは「T型を返しますよ」
という意味になります。
今回の演習では、この T を「配列の要素の型」として使っていきます。
Step 1:getFirst<T>() を作る
どんな配列でも「最初の要素」を返す汎用関数
まずは、配列の先頭要素を取り出す関数から始めます。
// 配列の最初の要素を返すジェネリック関数
function getFirst<T>(items: T[]): T | undefined {
// items: T[] なので、要素の型はT
if (items.length === 0) {
// 要素がない場合はundefinedを返す
return undefined;
}
return items[0]; // 最初の要素(T型)を返す
}
TypeScript重要ポイント:
- 引数
itemsの型はT[](T型の配列)です。 - 戻り値の型は
T | undefinedにしています。- 配列が空の場合もありえるので、「見つからない可能性」を型で表現しています。
実際に使ってみる
const numbers = [10, 20, 30];
const firstNumber = getFirst(numbers); // Tはnumberと推論される
console.log("最初の数値:", firstNumber); // 10
const names = ["Taro", "Hanako", "Jiro"];
const firstName = getFirst(names); // Tはstringと推論される
console.log("最初の名前:", firstName); // "Taro"
TypeScript配列の中身が数値でも文字列でも、 getFirst は「その配列の要素の型」をTとして扱ってくれます。
Step 2:getLast<T>() を作る
配列の「最後の要素」を返す汎用関数
次は、配列の末尾要素を取り出す関数です。
// 配列の最後の要素を返すジェネリック関数
function getLast<T>(items: T[]): T | undefined {
if (items.length === 0) {
return undefined;
}
return items[items.length - 1]; // 最後の要素(T型)を返す
}
TypeScriptこちらも同じく、
- 引数は
T[] - 戻り値は
T | undefined
という形になっています。
実際に使ってみる
const numbers2 = [10, 20, 30];
const lastNumber = getLast(numbers2); // Tはnumber
console.log("最後の数値:", lastNumber); // 30
const names2 = ["Taro", "Hanako", "Jiro"];
const lastName = getLast(names2); // Tはstring
console.log("最後の名前:", lastName); // "Jiro"
TypeScriptポイント:
getFirstとgetLastは、どちらも「配列の要素の型」をTとして扱っています。- 関数の中身はシンプルですが、Genericsのおかげで「どんな型の配列にも使える」汎用関数になっています。
Step 3:reverse<T>() を作る
配列を「逆順」にした新しい配列を返す汎用関数
次は、配列を逆順に並べ替える関数です。
// 配列を逆順にした新しい配列を返すジェネリック関数
function reverse<T>(items: T[]): T[] {
// 元の配列を壊さないように、コピーを作ってからreverseする
const copied = [...items]; // スプレッド構文でコピー
copied.reverse(); // 逆順に並べ替え
return copied; // T[] 型の配列を返す
}
TypeScript重要ポイント:
- 引数も戻り値も
T[]です。 - 元の配列を直接
reverse()せず、コピーしてから操作しているのは、 「副作用を減らす」ためのちょっとした工夫です。
実際に使ってみる
const numbers3 = [1, 2, 3, 4];
const reversedNumbers = reverse(numbers3); // Tはnumber
console.log("元の配列:", numbers3); // [1, 2, 3, 4]
console.log("逆順の配列:", reversedNumbers); // [4, 3, 2, 1]
const names3 = ["A", "B", "C"];
const reversedNames = reverse(names3); // Tはstring
console.log("元の配列:", names3); // ["A", "B", "C"]
console.log("逆順の配列:", reversedNames); // ["C", "B", "A"]
TypeScriptGenericsのおかげで、 「数値の配列」「文字列の配列」「オブジェクトの配列」など、 どんな配列でも同じ関数で扱えるようになっています。
Step 4:findById<T>() を作る
「idプロパティを持つオブジェクト配列」から、指定IDの要素を探す
最後は少しだけ応用編です。
idプロパティを持つオブジェクトの配列から、 指定したIDの要素を探す汎用関数
を作ってみます。
ここでは、Genericsに「制約」をつける書き方を使います。
// idプロパティを持つ型のための制約
type WithId = {
id: number;
};
// idで要素を検索するジェネリック関数
function findById<T extends WithId>(items: T[], id: number): T | undefined {
// Tは「id: numberを必ず持っている型」であることが保証される
return items.find((item) => item.id === id);
}
TypeScript深掘りポイント:
T extends WithIdという書き方は、 「Tは必ずid: numberを持っている型ですよ」という制約を意味します。- これにより、関数の中で
item.idを安全に使えるようになります。 - 「idを持たない型」は、この関数に渡せなくなります。
実際に使ってみる
type User = {
id: number;
name: string;
};
type Product = {
id: number;
title: string;
price: number;
};
const users: User[] = [
{ id: 1, name: "Taro" },
{ id: 2, name: "Hanako" },
];
const products: Product[] = [
{ id: 10, title: "Book", price: 1200 },
{ id: 11, title: "Pen", price: 200 },
];
const foundUser = findById(users, 2); // TはUser
const foundProduct = findById(products, 11); // TはProduct
console.log("見つかったユーザー:", foundUser); // { id: 2, name: "Hanako" }
console.log("見つかった商品:", foundProduct); // { id: 11, title: "Pen", price: 200 }
TypeScriptポイント:
findByIdは「idを持つ型なら何でも使える」汎用関数になっています。- UserでもProductでも、同じ関数で検索できるのに、 戻り値の型はちゃんと
UserやProductとして扱われます。 - これが「型を引数として扱う」Genericsの強みです。
Day 22 練習テンプレート:4つのジェネリック関数をまとめて試す
最後に、今回の4つの関数をひとつのファイルにまとめたテンプレートを載せます。 このままコピーして、配列の中身を変えたり、型を増やしたりしながら、 Genericsの感覚を育ててみてください。
// day22-generics-exercise.ts
// Day 22 演習:getFirst / getLast / reverse / findById
// 1. getFirst<T>()
function getFirst<T>(items: T[]): T | undefined {
if (items.length === 0) {
return undefined;
}
return items[0];
}
// 2. getLast<T>()
function getLast<T>(items: T[]): T | undefined {
if (items.length === 0) {
return undefined;
}
return items[items.length - 1];
}
// 3. reverse<T>()
function reverse<T>(items: T[]): T[] {
const copied = [...items];
copied.reverse();
return copied;
}
// 4. findById<T>()
type WithId = {
id: number;
};
function findById<T extends WithId>(items: T[], id: number): T | undefined {
return items.find((item) => item.id === id);
}
// ===== 使用例 =====
type User = {
id: number;
name: string;
};
type Product = {
id: number;
title: string;
price: number;
};
const users: User[] = [
{ id: 1, name: "Taro" },
{ id: 2, name: "Hanako" },
];
const products: Product[] = [
{ id: 10, title: "Book", price: 1200 },
{ id: 11, title: "Pen", price: 200 },
];
// getFirst / getLast
console.log("最初のユーザー:", getFirst(users));
console.log("最後の商品:", getLast(products));
// reverse
console.log("ユーザー配列(逆順):", reverse(users));
console.log("商品配列(逆順):", reverse(products));
// findById
console.log("ID=2のユーザー:", findById(users, 2));
console.log("ID=11の商品:", findById(products, 11));
TypeScriptDay 22 演習のまとめ
今回の演習では、
Tを「配列の要素の型」として扱うジェネリック関数の基本getFirst<T>()/getLast<T>()/reverse<T>()で、 どんな型の配列でも安全に扱える汎用関数を作る流れT extends WithIdという制約付きGenericsで、 「idを持つ型だけを受け付ける」findById<T>()を作る方法
を、具体的なコードとコメント付きで体験しました。
Genericsは、「同じロジックをいろいろな型で使い回す」ための強力な道具です。 今日の4つの関数をベースに、ぜひ自分なりに
filterByStatus<T>()mapToNames<T>()
など、オリジナルのジェネリック関数も考えてみてください。 そうしていくうちに、「型を引数として扱う」感覚が、自然と手に馴染んでいきます。

