TypeScript の型引数について

TwitterFacebookHatena

TL;DR

このページでは、TypeScript の型引数の実装方法について解説しますね。一言でいうと型引数とは、関数やクラスなどで使用される型を柔軟に指定できる考え方です。

開発環境 バージョン
Next.js 13.4.4
TypeScript 5.0.4
Emotion 11.11.0
React 18.2.0

型引数とは?

型引数とは、関数やクラスで使用される型を動的に指定できる変数です。これにより、再利用可能なコードを書く際に、柔軟に型を決定できます。

// ジェネリック型
const identity = <T>(value: T): T => {
  return value
}

// <number>や、<string>が型引数
const numberValue = identity<number>(42)
const stringValue = identity<string>('hello')

この例では、identity 関数に型引数 <T> を指定しています。この型引数は関数を呼び出す際に具体的な型として指定することができます。

ジェネリック型と型引数の違い

ジェネリック型と型引数は、TypeScript でよく用いられる考え方ですが、それぞれの役割と関係について説明しますね。

ジェネリック型とは?

ジェネリック型とは、型を引数として取ることができる型のことです。これによって、同じロジックで異なる型に対応する関数やクラスを作ることができます。

const identity = <T>(arg: T): T => {
  return arg
}

上記の例では、Tはジェネリック型引数であり、呼び出し時に具体的な型として置き換えられます。

型引数とは?

型引数は、ジェネリック型で用いられる変数のことで、具体的な型を指定するために使用されます。

const output = identity<string>('myString')

この例では、型引数としてstringを指定しています。この結果、関数identityは文字列型に特化した振る舞いをします。

ジェネリック型と型引数の関係

ジェネリック型は、関数やクラスがさまざまな型で動作できるように設計するための枠組みを提供します。一方、型引数は、その枠組みの中で具体的な型を指定するための変数です。

  • ジェネリック型は、型の再利用とコードの柔軟性を提供します。
  • 型引数は、ジェネリック型を使って具体的な型に対応する処理を行うための指定方法です。

ジェネリック型と型引数は、密接に関連していますが、同じものではありません。

  1. ジェネリック型(Generics): ジェネリック型は、型の一部を抽象化する機能です。ジェネリック型を定義するとき、型引数を使用して型の一部をプレースホルダーとして保持します。このプレースホルダーに具体的な型を割り当てることができます。

  2. 型引数(Type Arguments): 型引数は、ジェネリック型に対して具体的な型を割り当てるための値です。ジェネリック型に対して型引数を渡すことで、ジェネリック型が具体的な型になります。

以下の例で説明します。

// ジェネリック型の定義
function identity<T>(value: T): T {
  return value
}

// 型引数の使用
const numberValue = identity<number>(42) // ここで<number>は型引数

この例で言えば、identity<T> 関数はジェネリック型で、<number> は型引数です。ジェネリック型は抽象的な型の定義であり、型引数を通して具体的な型を与えることができるのです。

つまり、ジェネリック型は「型のテンプレート」のようなもので、型引数はそのテンプレートに対して具体的な型を与える役割を果たしています。彼らは一緒に働くもので、異なる側面を持っています。

複雑な型引数の使い方

次に、少し複雑なソースコードを書いてみましょう。以下の例は、型引数を使って配列の中から特定の型の要素を抽出する関数です。

type FilterType<T, U> = T extends U ? T : never

const filterByType = <T, U>(array: T[], type: U): FilterType<T, U>[] => {
  return array.filter((item) => item instanceof type) as FilterType<T, U>[]
}

const mixedArray = [1, 'apple', true, 42, 'orange']
const stringArray = filterByType<string | number | boolean, string>(mixedArray, String)

このコードの中で FilterType<T, U> という型を定義していますね。この型は、TU と同じ型であれば T を返し、そうでなければ never を返します。その後、この型を使用して filterByType 関数を定義しています。

React コンポーネントでの型引数

React コンポーネントにおいても、型引数は非常に役立ちます。次の例は、汎用的なリストコンポーネントを作成する際に型引数を使用しています。

type Props<T> = {
  items: T[]
  renderItem: (item: T) => React.ReactNode
}

const List = <T>({ items, renderItem }: Props<T>) => {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{renderItem(item)}</li>
      ))}
    </ul>
  )
}

このコンポーネントは、任意の型のアイテムを受け取り、それらをリストとしてレンダリングします。型引数 <T> は、コンポーネントが受け取るアイテムの型を指定するために使用されます。

TypeScript の型引数について