Next.js と TypeScript で、型ガードを扱う方法

TwitterFacebookHatena

TL;DR

このページでは、TypeScript の重要な要素の一つである「型ガード」について詳しく解説します。型ガードが何であるか、それがどのように動作し、そして何故それが役立つのか、具体的なソースコードを通じて一緒に学んでいきましょう。

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

「型ガード」 とは?

型ガードとは、TypeScript で使われる特別な機能で、ある変数が特定の型であることを確認するために使用されます。JavaScript は動的に型が変わる言語なので、一つの変数が異なる型の値を保持する可能性があります。しかし、TypeScript を使うと、コンパイル時に型のエラーを検出できます。それが型ガードの役割です。

型ガードの基本的な形式は以下の通りです。

if (typeof variable === 'type') {
  // variable is guaranteed to be 'type' within this block
}

これが型ガードの基本的な考え方で、このifブロック内ではvariabletypeとして扱われます。

使い所

型ガードは、以下のようなシチュエーションで有効に利用できます。

異なる型を持つ可能性がある変数の取り扱い

TypeScript では、異なる型を持つ変数を安全に扱うために、型ガードを使用することがよくあります。たとえば、ある関数が文字列または数値を返す可能性がある場合、その関数の戻り値を適切に処理するには、戻り値がどの型であるかを判断する必要があります。型ガードを使用すると、このような場合にコードの安全性を保証することができます。

ユーザー定義型の確認

複雑な型やユーザー定義の型を扱う場合、そのオブジェクトが特定の型を持つことを保証するために型ガードを使用することができます。この場合、型ガードはオブジェクトが必要なプロパティを持つことを確認し、型安全性を保証します。

API から返されるデータの取り扱い

外部 API からデータを取得する際、返されるデータの形式が確定していない場合もあります。型ガードを使用すると、API から返されるデータが予期した形式を持つことを確認し、データを安全に操作することができます。

コンポーネントのプロップスの型判定

React のコンポーネントでは、プロップスの型を判定するために型ガードを使用することがあります。これにより、異なるプロップスに応じて異なる動作をコンポーネントが実行することができます。

これらのケースは、型ガードが有効に利用できる例です。その他の多くのシチュエーションでも、型ガードはコードの安全性と可読性を向上させ、バグの発生を防ぐ強力なツールとなります。

Next.js で、型ガードを実装

それでは、具体的なコードを見てみましょう。Next.js と TypeScript を用いた型ガードの例を紹介します。

pages/index.tsx
import { NextPage } from 'next'

type Props = {
  data: string | number
}

const IndexPage: NextPage<Props> = ({ data }) => {
  if (typeof data === 'string') {
    // data is guaranteed to be a string here
    return <div>{data.toUpperCase()}</div>
  } else {
    // data is guaranteed to be a number here
    return <div>{data * 10}</div>
  }
}

export default IndexPage

このコードは Next.js のページコンポーネントで、型ガードを使用してdataが文字列型か数値型かを確認しています。ifブロック内では、dataは文字列として扱われ、そのため.toUpperCase()メソッドを安全に使用できます。一方、elseブロックでは、dataは数値として扱われ、そのため数値の演算を行うことができます。

型ガードを実装

さらに進んだ型ガードの実装を見ていきましょう。TypeScript ではユーザー定義の型ガードを作ることもできます。これにより、より複雑な型の検査が可能になります。

type User = {
  name: string
  age: number
}

function isUser(obj: any): obj is User {
  return obj && typeof obj.name === 'string' && typeof obj.age === 'number'
}

const IndexPage: NextPage<Props> = ({ data }) => {
  if (isUser(data)) {
    // data is guaranteed to be User here
    return (
      <div>
        {data.name} - {data.age}
      </div>
    )
  } else {
    // data is not User
    return <div>Not a user</div>
  }
}

export default IndexPage

ここでは、isUserという型ガード関数を定義し、dataUser型であることを検証しています。これにより、dataUser型であると確認された場所では、User型のプロパティに安全にアクセスすることができます。

Emotion で実装

最後に、Emotion という CSS-in-JS ライブラリを使った実装例を見てみましょう。このライブラリは、スタイルをコンポーネントレベルで管理することを可能にしています。

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
import { NextPage } from 'next'

type Props = {
  data: string | number
}

const textStyles = css`
  font-size: 18px;
  color: blue;
`

const IndexPage: NextPage<Props> = ({ data }) => {
  if (typeof data === 'string') {
    return <div css={textStyles}>{data.toUpperCase()}</div>
  } else {
    return <div css={textStyles}>{data * 10}</div>
  }
}

export default IndexPage

ここでは、dataの型に基づいて異なるテキストを表示していますが、どちらの場合も同じスタイル(textStyles)を使用しています。Emotion を使うことで、スタイルを再利用し、コンポーネントの見た目を一貫性を保つことができます。

以上が、Next.js と TypeScript を使って型ガードを活用する方法についての解説です。型ガードは TypeScript の強力な機能で、コードの安全性と予測可能性を高める重要なツールです。

Next.js と TypeScript で、型ガードを扱う方法