TL;DR
このページでは、Next.js と TypeScript の組み合わせにおける Union 型の活用方法について解説します。コードの可読性と保守性を向上させるテクニックとして、TypeScript の Union 型の知識は不可欠です。
開発環境 | バージョン |
---|---|
Next.js | 13.4.3 |
TypeScript | 5.0.4 |
Emotion | 11.11.0 |
React | 18.2.0 |
Union 型とは何か
Union 型は、TypeScript の型システムの一部で、一つの値が複数の型のいずれかを取りうることを表現します。たとえば、以下のように定義できます。
type Name = string | null
この型は、Name
が文字列または null となることを表しています。
Next.js における Union 型の利用
以下のコードは、Next.js のコンポーネントで Union 型を活用したシンプルな例ですね。
// components/Button.tsx
type Props = {
variant: 'primary' | 'secondary'
children: React.ReactNode
}
const Button = ({ variant, children }: Props) => {
const buttonStyle = variant === 'primary' ? 'bg-blue-500' : 'bg-green-500'
return <button className={buttonStyle}>{children}</button>
}
この Button
コンポーネントは、variant
プロパティを使って表示スタイルを切り替えます。variant
の型は、Union 型の 'primary' | 'secondary' で、この二つの値のみを許容します。これにより、想定外のスタイル指定を事前に排除でき、安全にコンポーネントを利用することができます。
Union 型を活用した高度なテクニック
さらに複雑なシナリオでは、Union 型を使って異なるプロパティの組み合わせをハンドリングすることもできます。以下にその一例をお見せします。
// components/ComplexComponent.tsx
type BaseProps = {
children: React.ReactNode
}
type VariantAProps = BaseProps & {
variant: 'A'
propA: string
}
type VariantBProps = BaseProps & {
variant: 'B'
propB: number
}
type Props = VariantAProps | VariantBProps
const ComplexComponent = (props: Props) => {
switch (props.variant) {
case 'A':
return <div>{props.propA}</div>
case 'B':
return <div>{props.propB}</div>
}
}
この ComplexComponent
コンポーネントは、プロパティ variant
の値に応じて異なるプロパティを必要とします。Union 型を活用することで、このような動的なプロパティセットを型安全に扱うことができます。
Emotion での Union 型の利用
Emotion を使う場合も、Union 型を活用することでスタイルのバリエーションを効果的に扱うことができます。以下にその一例をお見せします。
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
type Variant = 'info' | 'warning'
type Props = {
variant: Variant
children: React.ReactNode
}
const variantStyles: Record<Variant, ReturnType<typeof css>> = {
info: css`
background-color: lightblue;
color: darkblue;
`,
warning: css`
background-color: lightyellow;
color: darkred;
`,
}
const Alert = ({ variant, children }: Props) => {
return <div css={variantStyles[variant]}>{children}</div>
}
この Alert
コンポーネントは、variant
プロパティにより表示スタイルを切り替えます。Union 型を使って variant
の値を制限し、それに対応するスタイルを定義します。Emotion の css
関数を使って、それぞれのスタイルを切り替えます。
Discriminated Unions
ここでは、TypeScript の Union 型と Discriminated Unions(判別される Union 型)を使って、異なるステータスを持つ非同期データを取り扱う方法について説明します。このテクニックは、API リクエストの結果を扱う際に特に役立ちます。
まず、以下のように API リクエストのステータスを表現する型を定義します。
type DataStatus =
| { status: 'loading' } //
| { status: 'success'; data: any }
| { status: 'error'; error: Error }
ここでは、3 つのステータスを表現しています:
- データがまだロード中である(
loading
) - データのロードが成功し、データが利用可能である(
success
) - データのロードがエラーとなった(
error
)
次に、この型を使って非同期データを取り扱う React コンポーネントを作成します:
import React from 'react'
type DataStatus =
| { status: 'loading' } //
| { status: 'success'; data: any }
| { status: 'error'; error: Error }
type DataComponentProps = {
dataStatus: DataStatus
}
const DataComponent = ({ dataStatus }: DataComponentProps) => {
switch (dataStatus.status) {
case 'loading':
return <div>Loading...</div>
case 'success':
return <div>{JSON.stringify(dataStatus.data)}</div>
case 'error':
return <div>Error: {dataStatus.error.message}</div>
}
}
このコンポーネントは dataStatus
プロパティに基づいて異なる描画を行います。このプロパティは上で定義した DataStatus
型を持つため、TypeScript は switch
文内でそれぞれのケースに対する型を正しく推論することができます。これにより、例えば dataStatus.data
を loading
や error
のステータスで誤ってアクセスすると、TypeScript はこれをエラーとして報告します。
このように、TypeScript の Union 型と Discriminated Unions を使用することで、非同期データの取り扱いを型安全に行うことができます。これは API リクエストやその他の非同期処理を行う React コンポーネントで非常に役立つテクニックです。
以上が、Next.js と TypeScript における Union 型の利用法です。型の安全性を保ちながら、コードの可読性と保守性を向上させるために、Union 型を適切に活用することが重要です。