Next.js と TypeScript で、null と undefined を扱う

TwitterFacebookHatena

TL;DR

この記事では、Next.js と TypeScript を用いて、null と undefined の扱い方について解説します。実際のコード例とともに、その違いと正しい使い方を見ていきましょう。

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

null と undefined の違い

null と undefined は JavaScript における特殊な値で、それぞれ「値が存在しない」または「値がまだ設定されていない」ことを表現します。

null と undefined の扱い方の基本的な原則を理解すると、バグの発生を防ぐことができます。以下のコードは、null と undefined の比較について示しています。

const value1: number | null = null
const value2: number | undefined = undefined

console.log(value1 === null) // true
console.log(value2 === undefined) // true

console.log(value1 === value2) // false
console.log(value1 == value2) // true

このソースコードからわかるように、JavaScript では nullundefined== 比較で等しくなりますが、 === で厳密に比較すると等しくないです。このような特性を理解し、適切に利用することが大切です。

それでは、SNS でおなじみのトイレットペーパーを例に null と undefined の違いを試みてみましょう。

undefined

例えば、undefined は、あなたがトイレに行き、トイレットペーパーホルダーを見た時に何もない状況を指します。トイレットペーパーが設置されるべき場所に何も存在しない、つまり、存在自体が定義されていない状態。これが undefined です。

undefined は、変数が宣言されているが値がまだ設定されていないことを示すために使います。また、関数が明示的に何も返さない場合、その戻り値は undefined になります。

JavaScript と TypeScript における undefined は、以下のような状況で発生します。

変数が宣言されたが初期化されていない場合

let variable
console.log(variable) // 出力:undefined

ここでは variable が宣言されましたが、初期化はされていません。その結果、その値は undefined となります。

オブジェクトの未定義のプロパティにアクセスした場合

const obj = { name: 'Alice' }
console.log(obj.age) // 出力:undefined

ここでは objage プロパティを持っていません。そのため、obj.age の値は undefined となります。

何も返さない関数の戻り値

function greet() {
  console.log('Hello, world!')
}

const result = greet()
console.log(result) // 出力:undefined

ここでは greet 関数は何も返さないため、その戻り値は undefined となります。

これらのシナリオはすべて、何かが未定義(設定されていない、存在しない、返されていないなど)であることを示す場合に undefined が使用されます。したがって、undefined は通常、何かがまだ設定されていないこと、または本質的に存在しないことを示すために使用されます。

null

一方で、null はトイレットペーパーが設置はされているものの、すでに紙が全て使い果たされ、空の芯だけが残っている状況を表します。ここでトイレットペーパーホルダー自体は存在する(つまり定義されている)のですが、その中身(トイレットペーパー)が無い状態、これが null です。

null は、ある変数が「存在するが値が無い」ことを示すために使います。例えば、ユーザーのプロフィール画像の URL を表す変数があり、ユーザーがプロフィール画像を設定していない場合、その変数には null を設定することが適切です。

JavaScript および TypeScript において null は、値が故意的に「存在しない」または「空」であることを示すために使われます。以下に具体的な状況を示します。

変数に明示的に null を設定した場合

let variable = null
console.log(variable) // 出力:null

この場合、variable は明示的に null と設定されています。つまり、variable は存在するが、値がないことを示しています。

オブジェクトのプロパティを明示的に null に設定した場合

const obj = { name: null }
console.log(obj.name) // 出力:null

ここでは objname プロパティが明示的に null と設定されています。つまり、name プロパティは存在するが、その値はないことを示しています。

関数が明示的に null を返す場合

function getNull() {
  return null
}

const result = getNull()
console.log(result) // 出力:null

ここでは getNull 関数が明示的に null を返しています。したがって、その戻り値は null です。

これらのシナリオは、何かが故意に存在しないまたは空であることを示す場合に null が使用されます。したがって、null は通常、何かが明示的に存在しないことまたは空であることを示すために使用されます。

この例からも分かるように、undefined と null は似ているようで実は異なる特性を持つということが理解できると思います。

let value1: number | null = null // 値が存在しないことを表現
let value2: number | undefined // 値がまだ設定されていないことを表現

ただし、これらの値は似ているようで、用途と振る舞いに違いがあります。

  • null: 明示的に値がない、または空であることを示すために使用します。
  • undefined: 値が未定義、またはまだ設定されていないことを示すために使用します。

Next.js で、null と undefined を扱う

Next.js と TypeScript で実装する場合、null と undefined の扱い方は重要になります。以下のコードは、Next.js のページコンポーネントの一例です。

ファイル名: pages/index.tsx

import { GetServerSideProps } from 'next'

type Props = {
  message: string | null
}

const HomePage = ({ message }: Props) => {
  return <div>{message ? <p>{message}</p> : <p>No message.</p>}</div>
}

export const getServerSideProps: GetServerSideProps = async () => {
  // 外部 API からメッセージを取得する想定
  const message = await fetchMessageFromAPI()

  return {
    props: {
      message: message || null,
    },
  }
}

export default HomePage

このソースコードは、外部 API からメッセージを取得して、そのメッセージを表示する Next.js のページを作成します。ただし、API からのメッセージが存在しない場合は、null を返し、「No message.」と表示します。

このように、null と undefined を適切に扱うことで、存在しない値を安全に扱うことができます。

Emotion で、null と undefined を扱う

次は、Emotion を利用した複雑なソースコード例です。この例では、null と undefined の扱いに加えて、CSS-in-JS の概念も取り入れてみましょう。

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

type User = {
  id: string
  name: string
  avatarUrl: string | null
}

type Props = {
  user: User | null
}

const userStyles = css`
  display: flex;
  align-items: center;
`

const avatarStyles = (url: string | null) => css`
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background-image: url(${url});
  background-size: cover;
  background-position: center;
  margin-right: 15px;
`

const HomePage = ({ user }: Props) => {
  return (
    <div>
      {user ? (
        <div css={userStyles}>
          <div css={avatarStyles(user.avatarUrl || undefined)}>{/* If avatarUrl is null, display nothing */}</div>
          <p>{user.name}</p>
        </div>
      ) : (
        <p>No user found.</p>
      )}
    </div>
  )
}

export const getServerSideProps: GetServerSideProps = async () => {
  // Fetch a user from an external API
  const user = await fetchUserFromAPI()

  return {
    props: {
      user: user || null,
    },
  }
}

export default HomePage

このコードでは、外部 API から取得したユーザー情報を表示します。ユーザーのアバター画像の URL が null の場合、デフォルトの背景画像を表示しないようにしています。このように、null と undefined の扱い方を理解することで、複雑な状況でも適切に対応することができるんですよね。

また、Emotion を使って動的にスタイルを適用する例も見せています。avatarStyles 関数では、背景画像の URL に null が来た場合に undefined を返すことで、CSS の背景画像が適用されないようにしています。

Next.js と TypeScript で、null と undefined を扱う