Next.js と TypeScript で、keyof オペレーターを理解する

TwitterFacebookHatena

TL;DR

このページでは、TypeScript の keyof オペレーターの実装方法について詳しく解説します。一言でいうと keyof とは、あるオブジェクトのすべてのプロパティ名の型を抽出する TypeScript のオペレーターです。

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

keyof オペレーターとは?

keyof オペレーターは、TypeScript が持つ特別な型演算子の一つで、オブジェクトの全てのプロパティ名(キー)の型を抽出します。これにより、動的なプロパティのアクセスや、特定のオブジェクトが持つプロパティの名前に制限を加えることなどが可能になります。

基本的な使い方を見てみましょう。

type Animal = {
  name: string
  age: number
}

type AnimalKeys = keyof Animal // "name" | "age"

ここでは、Animal という型のオブジェクトがあり、keyof を使ってそのプロパティ名の型を AnimalKeys という型として抽出しています。結果として AnimalKeys"name" | "age" という型になります。これは、AnimalKeys"name" または "age" の文字列しか受け取ることができないということを意味します。

keyof のメリット

TypeScript の keyof の主なメリットは以下の通りです:

  1. 型安全: keyof を使うことで、オブジェクトのキーへのアクセスが型安全になります。これにより、存在しないキーへのアクセスや誤った型へのアクセスによるランタイムエラーを防ぐことができます。

  2. コードの再利用性: keyof を使うことで、オブジェクトのキーに対して一般的な操作を行う関数やコンポーネントを作成することができます。これにより、同じ操作を異なるキーに対して行うためにコードを繰り返すことなく、一つの関数やコンポーネントを再利用できます。

  3. コードの可読性と保守性: keyof を使うことで、どのキーがアクセス可能かが型として明示的に表現されるため、コードの可読性と保守性が向上します。

さらにシンプルな例を以下に示します:

type Person = {
  name: string
  age: number
}

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

let person: Person = { name: 'John Doe', age: 25 }

let personName = getProperty(person, 'name') // personName is of type 'string'
let personAge = getProperty(person, 'age') // personAge is of type 'number'

この例では、getProperty関数はオブジェクトとそのキーをパラメータとして受け取り、そのキーに対応するプロパティの値を返します。この関数はどのようなオブジェクトとキーでも動作しますが、keyof のおかげで、存在しないキーを渡そうとするとコンパイルエラーが発生します。これにより、コードの安全性が向上します。

さらに、この関数は引数 key の型を動的に判断し、それに基づいて戻り値の型を決定します。このため、戻り値を適切に型付けすることができます。このような動的な型付けは、型安全性を保ちながら JavaScript のような柔軟性を得ることができる TypeScript の強力な特性です。

Next.js で、keyof オペレーターを実装

次に、具体的な Next.js のコードにこの keyof オペレーターを導入してみましょう。データを取得する API の呼び出しとその結果の表示を行うシンプルなコンポーネントを作成します。

ファイル名:components/AnimalViewer.tsx

import { useEffect, useState } from 'react'

type Animal = {
  name: string
  age: number
}

type AnimalKeys = keyof Animal

const AnimalViewer = () => {
  const [animal, setAnimal] = useState<Animal | null>(null)
  const [key, setKey] = useState<AnimalKeys>('name')

  useEffect(() => {
    fetch('/api/animal')
      .then((res) => res.json())
      .then((data) => setAnimal(data))
      .catch((error) => console.error(error))
  }, [])

  if (!animal) {
    return <div>Loading...</div>
  }

  return (
    <div>
      <button onClick={() => setKey('name')}>Show Name</button>
      <button onClick={() => setKey('age')}>Show Age</button>
      <div>{animal[key]}</div>
    </div>
  )
}

export default AnimalViewer

このコンポーネントは、まず /api/animal から動物のデータを取得し、取得したデータは animal の状態に保存します。データは nameage という二つのプロパティを持つオブジェクトです。

また、key という状態を持ち、これは表示するプロパティの名前を保持します。key の型は AnimalKeys として、"name" | "age" のいずれかの値しか取らないことが保証されています。そのため、animal[key] という動的なプロパティアクセスが型安全に行えます。

このコンポーネントには二つのボタンがあり、それぞれが key の状態を "name" または "age" に設定します。そして、animal[key] の値を表示します。これにより、ボタンを押すことで表示されるプロパティが切り替わる、動的な表示が可能になります。

別の活用例:ソート関数の作成

keyof は他にも様々な場面で活用できます。例えば、オブジェクトの配列を任意のプロパティでソートする関数を作ることができます。

type Student = {
  name: string
  age: number
  grade: number
}

type StudentKeys = keyof Student

const sortStudents = (students: Student[], key: StudentKeys) => {
  return students.sort((a, b) => (a[key] > b[key] ? 1 : -1))
}

ここでは、生徒のデータを表す Student 型と、そのプロパティ名の型 StudentKeys を定義しました。そして、生徒の配列とソートするプロパティの名前を受け取り、そのプロパティでソートした結果を返す関数 sortStudents を作りました。

こうした型を活用することで、コードの安全性を向上させるとともに、あらゆる場面での汎用性を保つことが可能となります。JavaScript における柔軟性を損なうことなく、型の恩恵を受けられる TypeScript の強力な機能の一つである keyof について、ここまで学んできました。

以上、Next.js と TypeScript を使って keyof オペレーターを活用する方法について解説しました。この力強い機能を駆使することで、より安全で再利用可能なコードを書くことができるようになるでしょう。これからも Next.js と TypeScript を使って、効果的なプログラミングを続けていきましょう。

Next.js と TypeScript で、keyof オペレーターを理解する