Next.js と TypeScript で、型推論を理解する

TwitterFacebookHatena

TL;DR

今回は「型推論」について一緒に楽しく学んでいきましょう!

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

「型推論」って何?

型推論っていうのは、コンピュータが魔法使いみたいになってくれて、我々が書くコードの「型」を自動的に判断してくれる技術です。これは TypeScript という言語に備わっている機能の一つなんですね。

まずは、「型」って何?から解説しましょう。「型」とは、数値や文字列など、データの種類を指す言葉です。例えば、数字の 5 は「数値型」、"hello" という文字は「文字列型」なんです。

let number: number = 5 // これは「数値型」
let text: string = 'hello' // これは「文字列型」

さて、ここで型推論の出番です。TypeScript はスマートなので、我々が型を指定しなくても、自動的にそれを推測してくれます。

let number = 5 // TypeScript はここが「数値型」だと推測してくれる
let text = 'hello' // ここは「文字列型」だと推測してくれる

こういう風に、型を明示的に書かなくても型を理解してくれるのが「型推論」の力です。

型推論を使えば、型を指定しなくてもよいのか?

型推論を使用すると、TypeScript はコードから変数の型を推測しようとします。このため、型宣言を必要としない場合があります。

しかし、これは型宣言が不要という意味ではありません。型推論は一部のシナリオで非常に有用ですが、常にすべてのケースで正確に型を推測できるわけではありません。また、明示的な型宣言はコードの可読性を向上させ、あなたの意図を明確に伝えることができます。

たとえば、以下のコードでは、TypeScript はresultの型を推測できます。

const add = (a: number, b: number) => {
  return a + b
}

const result = add(1, 2)

この場合、resultadd関数の結果を保持しているため、TypeScript はそれがnumber型であると自動的に推測します。そのため、resultの型を明示的に宣言する必要はありません。

しかし、型推論は常に十分ではない場合があります。例えば、関数の引数や複雑なオブジェクトについては、引数の型やオブジェクトの形状を明示的に宣言したほうが良い場合があります。これにより、エラーを早期にキャッチしたり、関数が何を期待しているのかを他の開発者に明確に伝えることができます。

要するに、型推論は非常に有用ですが、それだけに依存するのではなく、適切な場所で明示的な型宣言を行うことが、TypeScript を最大限に活用するための良い戦略です。

次の例を見てみましょう。

const a = 2 + 3

上の例では、数値 2 と 3 を加算しています。その結果を変数aに格納しています。しかし、このコードには明示的な型宣言がありません。それでも TypeScript は型推論の力を使って、aの型がnumberであると自動的に認識します。

なぜなら、JavaScript(そして TypeScript も)は、2 と 3 のようなリテラル値が数値であると認識し、それらを加算するときに結果もまた数値になると理解しているからです。

そのため、次のように書いても結果は同じです。

const a: number = 2 + 3

ただし、この場合はaの型を明示的に指定しています。このように型を明示的に書くことで、もし間違えた値を代入しようとした場合、TypeScript が型チェックを行い、エラーを通知してくれます。

TypeScript の型推論は、我々が明示的に型を書かなくても、適切な型を TypeScript が推測してくれるという強力な機能です。しかし、コードの可読性や型の安全性を確保するために、場合によっては型を明示的に書くことも重要です。

リテラルと型の対応表

それぞれのリテラルとその対応する TypeScript の型を示した表を以下に作成しました。

リテラル TypeScript の型
"Hello" string
123 number
true / false boolean
{} object
null null
undefined undefined
[] any[]
["Hello"] string[]
[123, 456] number[]

上記表は一部を表しています。TypeScript では複雑な型も表現できます。たとえばオブジェクトや配列の中にさまざまな型の値を格納することができます。また、カスタム型を定義することも可能です。

// オブジェクトの例
const obj: { name: string; age: number } = { name: 'Alice', age: 20 }

// 配列の例
const arr: (string | number)[] = ['Alice', 20]

このように、TypeScript は JavaScript の動的な型システムに静的型付けの能力を加えることで、より安全で予測可能なコードを書くのを助けてくれます。

コンポーネント呼び出し先の型推論

TypeScript は React コンポーネントの開発においても強力なツールとなります。特に、コンポーネントの props(プロパティ)に型を付けることで、エラーを早期に検出するのに役立ちます。また、コンポーネントが期待する props の型を明示的に示すことで、他の開発者がコンポーネントを正しく使う助けにもなります。

次に示すように、TypeScript で React コンポーネントを作るときには、まずコンポーネントが受け取る props の型を定義します。

type ButtonProps = {
  onClick: () => void
  label: string
}

const Button = ({ onClick, label }: ButtonProps) => <button onClick={onClick}>{label}</button>

ここで、ButtonPropsという型を定義し、それをButtonコンポーネントに適用しています。このButtonPropsは 2 つのフィールドを持っています:onClickは関数であり、labelは文字列です。

さて、このコンポーネントを呼び出すとき、TypeScript の型推論が役立ちます。

<Button onClick={() => console.log('clicked!')} label="Click me" />

このコードを見て、TypeScript はButtonコンポーネントが期待する props の型(つまりButtonProps)を見て、渡された props がそれと一致するかどうかを確認します。この場合、onClicklabelの両方が適切な型で提供されているため、問題ありません。

しかし、もしlabelとして数値を渡そうとすると、

<Button onClick={() => console.log('clicked!')} label={123} />

ここで、TypeScript はButtonコンポーネントがlabelとして文字列を期待していることを知っているので、型エラーをすぐに報告します。これにより、開発者はコードをデプロイする前にこのエラーを修正できます。

このように、TypeScript の型推論はコンポーネントの props の型の一貫性を保つための強力なツールとなります。

Next.js で、「型推論」を使う

さて、Next.js の中で型推論をどのように使っていくか見ていきましょう。まずは、簡単なコンポーネントを作ってみよう。

// pages/index.tsx

type Props = {
  name: string
}

const HomePage = ({ name }: Props) => {
  return <div>Hello, {name}!</div>
}

export default HomePage

このコンポーネントは、name というプロパティを受け取り、それを画面に表示するようになっています。name の型は string(文字列型)となっています。

この HomePage コンポーネントは、Next.js が自動的に / のパスで表示されるようになります。そして、name プロパティを渡すことで、「Hello, [name]!」と表示されます。

さて、ここで型推論の力を見せる時がきました。HomePage コンポーネントに name プロパティを渡す時、TypeScript はその型を推測してくれます。

// pages/index.tsx

const HomePage = ({ name }) => {
  // TypeScript は `name` が `string` 型だと推測してくれます
  return <div>Hello, {name}!</div>
}

export default HomePage

このように、型推論を利用すれば、コードをシンプルに保ちつつ、安全性を確保することができます。

「型推論」を使うテクニカルな例

型推論は、もっと複雑な場面でも役立ちます。例えば、関数の引数や戻り値の型を自動的に推測することができます。次の例を見てみましょう。

// utils/calculate.ts

type CalculateProps = {
  num1: number
  num2: number
}

const calculateSum = ({ num1, num2 }: CalculateProps) => {
  return num1 + num2
}

export default calculateSum

この calculateSum 関数は、num1num2 を受け取り、その和を返します。TypeScript は、この関数の引数と戻り値の型を自動的に推測してくれます。

// utils/calculate.ts

const calculateSum = ({ num1, num2 }) => {
  // TypeScript は引数の型と戻り値の型を推測してくれます
  return num1 + num2
}

export default calculateSum

これにより、関数を使う側は、引数と戻り値の型を意識せずにコーディングを進めることができます。型推論は、より良い開発体験を提供します。

Emotion での実装

最後に、Emotion を用いた型推論の活用例を見ていきましょう。Emotion は、CSS-in-JS のライブラリで、スタイルの定義も JavaScript で行います。これにより、型推論の恩恵を受けられます。

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

type ButtonProps = {
  color: string
}

const Button = ({ color }: ButtonProps) => {
  return (
    <button
      css={css`
        background-color: ${color};
      `}
    >
      Click me
    </button>
  )
}

export default Button

この Button コンポーネントは、color プロパティを受け取り、それを背景色に適用します。TypeScript は color プロパティの型を自動的に推測します。

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

const Button = ({ color }) => {
  // TypeScript は `color` が `string` 型だと推測してくれます
  return (
    <button
      css={css`
        background-color: ${color};
      `}
    >
      Click me
    </button>
  )
}

export default Button

Emotion を使っている場合でも、TypeScript の型推論はしっかりと機能します。これにより、スタイリングに関するコードも、より簡潔で安全に保つことができます。

以上が、Next.js と TypeScript を組み合わせた際の、型推論の利用例となります。初心者でもわかるように解説してきましたが、どうでしょうか?型推論は、コードの安全性を確保しつつ、コードをシンプルに保つ強力なツールです。是非、あなたのコーディングに活用してみてくださいね!

Next.js と TypeScript で、型推論を理解する