見出し画像

TypeScriptジェネリクスをサクッと理解【その3 型定義とfunction両方に対して使うジェネリクス】

TypeScriptのジェネリクスはむずかしい?

この記事はジェネリクスをサクッと解説する全4回の記事の3つ目です。

1 → TypeScriptジェネリクスをサクッと理解【その1 型定義に対して使うジェネリクス】

2 → TypeScriptジェネリクスをサクッと理解【その2 functionに対して使うジェネリクス】

*3(本記事)→ TypeScriptジェネリクスをサクッと理解【その3 型定義とfunction両方に対して使うジェネリクス】

4 → TypeScriptジェネリクスをサクッと理解【その4 複数のジェネリクスをつかう】

型定義とfunction両方のジェネリクス

次のfunctionがあったとします。

// main.ts

function example(b: any){
    return b.data
}

これは、渡された「b」というany型のデータをreturnするだけのfunctionですが、「b.data」となっているところから、「b」はオブジェクトだとわかります。

実際の動きを見るには、次のコードを追加しましょう。

// main.ts

// 👇追加
const userOne: any = {
    data: "太郎",
    color: "赤"
}
// 👆追加

function example(b: any){
    return b.data
}

// 👇追加
console.log(
    example(userOne)
)
// 👆追加

そしてJavaScriptに変換して実行すると、ブラザウのコンソールには「太郎」と表示されます。「b.data」、つまり「userOne.data」は「太郎」だからです。

ここで次のデータを足してみます。ここからは1回目で触れた「型定義のジェネリクス」の復習となります。

// main.ts

const userOne: any = {
    data: "太郎",
    color: "赤"
}

// 👇追加
const userTwo: any = {
    data: 2002,
    color: "緑"
}
// 👆追加

function example(b: any){
    return b.data
}

console.log(
    example(userOne)
)

// 👇追加
console.log(
    example(userTwo)
)
// 👆追加

anyとなっているところにジェネリクスを使って型情報を追加しましょう。まずuserOneとuserTwoの型定義です。

繰り返しになりますが、ここでジェネリクスを使う理由は、stringとnumberという異なる型が「data」に入っているuserOne、userTwo両方に適用可能な柔軟な型定義を作るためです。

// main.ts

// 👇追加
type User<T> = {
    data: T;
    color: string;
}
// 👆追加

const userOne: User<string> = {     // 追加
    data: "太郎",
    color: "赤"
}

const userTwo: User<number> = {     // 追加
    data: 2002,
    color: "緑"
}

function example(b: any){
    ...

次はfunctionのanyを修正しましょう。

まず「b」というオブジェクトに型定義Userを当てはめます。

function example(b: User){
    return b.data
}

次のエラーが出ます。

エラー

「Generic type 'User' requires 1 type argument(s)」とは「Userには型引数がひとつ必要です」という意味なので、型引数である<T>を追加します。

                        👇追加
function example(b: User<T>){
    return b.data
}

returnするデータの型も追加します。

returnする「b.data」は、userOneであれば「太郎」というstring、userTwoであれば「2002」というnumberといったように定まっていないので、「型はあとで決める」という柔軟さを得られるジェネリクスを使い、次のように書きましょう。

                             👇追加
function example(b: User<T>): T{
    return b.data
}

最後に、このfunctionがジェネリクスを使っていることを明示する必要があるので、さらにもうひとつ「T」を書き足します。

              👇追加
function example<T>(b: User<T>): T{
    return b.data
}

これが型定義とfunctionの両方にジェネリクスを使ったケースになります。

いまコード全体は次のようになっています。

// main.ts

type User<T> = {
    data: T;
    color: string;
}

const userOne: User<string> = {
    data: "太郎",
    color: "赤"
}

const userTwo: User<number> = {
    data: 2002,
    color: "緑"
}

function example<T>(b: User<T>): T{
    return b.data
}

console.log(
    example(userOne)
)

console.log(
    example(userTwo)
)

ジェネリクスは複数使うこともできるので、これは次回の本連載最終回で見てみましょう。


2024年3月React + TypeScriptの新刊書をリリースしました。

ジェネリクスを含む、基礎から応用までの幅広い範囲をカバーしており、TypeScript + React入門者から、中級者の人にもっとも学びがある内容になっています。

https://monotein.base.shop/p/00002

三好アキ『React + TS徹底フォーカス:TypeScriptマスター with React』

この記事の著者については下記リンクをチェック

◆ Amazon著者ページ:https://www.amazon.co.jp/stores/author/B099Z51QF2

公式サイト:https://monotein.com
Twitter:https://twitter.com/monotein_

React、Next.js、TypeScriptなどのお役立ち情報や実践的コンテンツを、ビギナー向けにかみ砕いて無料配信中。メルマガ登録はこちらから → https://monotein.com/register-newsletter

この記事が気に入ったらサポートをしてみませんか?