Next.js と TypeScript で、スプレッド構文の効果的な使い方

TwitterFacebookHatena

TL;DR

このページでは、JavaScript と TypeScript の中で多用される「スプレッド構文」の実装方法について解説します。スプレッド構文は配列やオブジェクトの要素を展開したり、合成したりする際に活躍します。それでは早速、Next.js と TypeScript の環境でスプレッド構文の使い方を見ていきましょう。

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

スプレッド構文とは?

スプレッド構文とは、JavaScript および TypeScript において配列やオブジェクトの要素を一つずつ展開したり、新たな配列やオブジェクトを作成したりするための構文です。具体的な基本的なコードは以下の通りです。

const array1 = [1, 2, 3]
const array2 = [...array1, 4, 5] // [1, 2, 3, 4, 5]

const obj1 = { a: 1, b: 2 }
const obj2 = { ...obj1, c: 3 } // { a: 1, b: 2, c: 3 }

イミュータブル性とスプレッド構文

JavaScript では、文字列や数字は、イミュータブル(変更不可)、配列とオブジェクトはデフォルトでミュータブル(変更可能)になっています。

最近の JavaScript では、ミュータブルが「悪」とされているので、特に関数型プログラミングの影響を受けて、ミュータブルを避けて、イミュータブルなプログラミングをすることが推奨されているんですね。

そういったこともあって、配列やオブジェクトのコピーや結合には、破壊的メソッドを避けて、スプレッド構文が使われる傾向にあります。

イミュータブルな状態とは、一度作られたデータが後から変更されない状態のことを指します。これは配列やオブジェクトのようなデータ構造に対して、特に重要な概念となります。

スプレッド構文は、このイミュータブルなプログラミングを実現するための重要なツールとなっています。スプレッド構文を使うと、配列やオブジェクトの新しいコピーを作成し、そのコピーに変更を加えることができます。そのため、元の配列やオブジェクトは変更されません。これは「イミュータブル」な状態を保つのに役立ちます。

const originalArray = [1, 2, 3]
const newArray = [...originalArray, 4] // originalArray は変更されない

const originalObj = { a: 1, b: 2 }
const newObj = { ...originalObj, c: 3 } // originalObj は変更されない

イミュータブルな状態がもたらす利点は多く、その一つが「挙動の予測可能性」です。イミュータブルなデータは一度作られた後に変更されませんので、データがどの時点で何になっているかを追いやすくなります。これはデバッグを容易にし、バグの発生を抑制します。

また、イミュータブルな状態を使うことで複雑な処理を簡略化できます。配列やオブジェクトを直接変更することなく、新しいコピーを作成して操作することで、データの流れをより直感的に理解することが可能となります。

さらに、React のようなライブラリやフレームワークでは、状態の変更を検出して UI を再描画するために、イミュータブルな状態が使用されます。新旧の状態を比較することで変更を検出し、変更があった部分のみを効率よく再描画します。

イミュータブルな状態はスレッドセーフな状態を実現します。これは、同時に複数の処理がデータにアクセスしても、データが変更されないために競合状態を防ぐことができるということを意味します。

Next.js で、スプレッド構文を実装

それでは具体的に Next.js と TypeScript でスプレッド構文を使ってみましょう。以下に示すコードは、React のコンポーネントで props を受け取り、スプレッド構文を使ってその props を子コンポーネントに渡す例です。

ファイル名:components/ParentComponent.tsx

type ParentProps = {
  name: string
  age: number
}

const ParentComponent = ({ name, age }: ParentProps) => {
  return <ChildComponent {...{ name, age }} />
}

type ChildProps = ParentProps

const ChildComponent = ({ name, age }: ChildProps) => {
  return (
    <div>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
    </div>
  )
}

このコードにより、親コンポーネント

から子コンポーネントへ props が渡されます。親コンポーネントの props をそのまま子コンポーネントに渡している部分に注目してください。ここではスプレッド構文を使って props を展開しています。

この例から分かるように、スプレッド構文を用いることで、コードの可読性が向上し、コード量も削減できます。また、可変長の引数を渡す場合やデフォルトの値を上書きする場合などにも活用できます。

Next.js でのコンポーネントの活用

さらに、Next.js を使用したスプレッド構文の活用例として、React のコンポーネントでプロパティを扱う方法を見ていきましょう。スプレッド構文を使用することで、プロパティの受け渡しが容易になり、コードがより明快になります。

ここでは Next.js プロジェクトでよく見られるカードコンポーネントの例をご紹介します。

// components/Card.tsx
type CardProps = {
  title: string
  description: string
  imageUrl: string
}

const Card = ({ title, description, imageUrl }: CardProps) => {
  return (
    <div>
      <img src={imageUrl} alt={title} />
      <h2>{title}</h2>
      <p>{description}</p>
    </div>
  )
}

export default Card

ここで定義したCardコンポーネントは、titledescriptionimageUrlの 3 つのプロパティを受け取ります。スプレッド構文を使用して、これらのプロパティを一度に受け取ることができます。

次に、このカードコンポーネントを使用するページを作成します。

// pages/index.tsx
import Card from '../components/Card'

const HomePage = () => {
  const cardProps = {
    title: 'Welcome to Next.js!',
    description: 'This is a sample card component.',
    imageUrl: '/logo.png',
  }

  return <Card {...cardProps} />
}

export default HomePage

このHomePageコンポーネントでは、スプレッド構文を使用してcardPropsオブジェクトの全てのプロパティをCardコンポーネントに一度に渡しています。このようにスプレッド構文を使用することで、コードの見通しが良くなり、保守性も向上します。

以上が Next.js と TypeScript を使ったスプレッド構文の活用例です。このようにスプレッド構文は、コードをよりシンプルに保つための強力なツールとなります。

スプレッド構文を使用する際は、展開する対象のデータタイプ(配列、オブジェクト)を正しく理解することが重要ですよ。間違えると予期せぬエラーにつながることもありますので、注意して使ってください。

Next.js と TypeScript で、スプレッド構文の効果的な使い方