Menu

TypeScript

Next.jsには組み込みのTypeScriptが付属しており、create-next-appで新しいプロジェクトを作成する際に必要なパッケージを自動的にインストールし、適切な設定を構成します。

既存プロジェクトにTypeScriptを追加するには、ファイル名を.ts/.tsxに変更します。next devnext buildを実行すると、必要な依存関係が自動的にインストールされ、推奨される設定オプション付きのtsconfig.jsonファイルが追加されます。

補足:既にjsconfig.jsonファイルが存在する場合は、古いjsconfig.jsonからpathsコンパイラオプションを新しいtsconfig.jsonファイルにコピーし、古いjsconfig.jsonファイルを削除してください。

IDEプラグイン

Next.jsにはカスタムTypeScriptプラグインと型チェッカーが含まれており、VSCodeおよび他のコードエディタが高度な型チェックと自動補完に使用できます。

VS Codeでプラグインを有効にするには:

  1. コマンドパレットを開きます(Ctrl/⌘ + Shift + P
  2. 「TypeScript: Select TypeScript Version」を検索します
  3. 「Use Workspace Version」を選択します
TypeScript Command Palette

これでファイルを編集するときにカスタムプラグインが有効になります。next buildを実行するときは、カスタム型チェッカーが使用されます。

TypeScriptプラグインは以下を支援できます:

  • セグメント設定オプションの無効な値が渡される場合に警告します。
  • 利用可能なオプションとコンテキスト内ドキュメントを表示します。
  • 'use client'ディレクティブが正しく使用されていることを確認します。
  • クライアントフック(useStateなど)がクライアントコンポーネントでのみ使用されていることを確認します。

🎥 ウォッチ:組み込みTypeScriptプラグインについて学ぶ → YouTube(3分)

エンドツーエンドの型安全性

Next.js App Routerは強化された型安全性を備えています。これには以下が含まれます:

  1. フェッチ関数とページ間のデータシリアライゼーションなし:サーバー上のコンポーネント、レイアウト、ページでデータを直接fetchできます。このデータはクライアント側でReactで使用するために渡されるために、シリアライズされる(文字列に変換される)必要が「ありません」。代わりに、appはデフォルトではServer Componentsを使用しているため、余分な手順なしにDateMapSetなどの値を使用できます。以前は、サーバーとクライアントの境界をNext.js固有の型で手動で型付けする必要がありました。
  2. コンポーネント間の簡素化されたデータフロー_appが削除されてルートレイアウトが採用されたことで、コンポーネントとページ間のデータフローを視覚化するのが容易になりました。以前は、個々のpages_appの間でフローするデータは型付けが難しく、混乱を招くバグが発生する可能性がありました。App Routerのコロケーションされたデータフェッチにより、これはもはや問題ではありません。

Next.jsでのデータフェッチは、データベースやコンテンツプロバイダーの選択について規定的にならずに、可能な限りエンドツーエンドの型安全性に近づけることができます。

通常のTypeScriptで期待されるように、レスポンスデータを型付けできます。例えば:

app/page.tsx
TypeScript
async function getData() {
  const res = await fetch('https://api.example.com/...')
  // 戻り値は「シリアライズされていません」
  // Date、Map、Setなどを返すことができます
  return res.json()
}
 
export default async function Page() {
  const name = await getData()
 
  return '...'
}

完全なエンドツーエンドの型安全性については、これはデータベースまたはコンテンツプロバイダーがTypeScriptをサポートしている必要があります。これはORMまたは型安全なクエリビルダーを使用することで実現できます。

ルート認識型ヘルパー

Next.jsはApp Routerルート型のグローバルヘルパーを生成します。これらはインポートなしで利用でき、next devnext build、またはnext typegenによって生成されます:

Next.js設定ファイルの型チェック

next.config.tsを使用して、TypeScriptをインポートし、Next.js設定で型を使用できます。

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  /* config options here */
}
 
export default nextConfig

next.config.ts内のモジュール解決は現在、CommonJSに限定されています。ただし、ECMAScriptモジュール(ESM)構文は、Node.js v22.10.0以上でNode.jsネイティブTypeScript解決器を使用する場合に利用できます。

next.config.jsファイルを使用する場合は、以下のようにJSDocを使用してIDEで型チェックを追加できます:

next.config.js
// @ts-check
 
/** @type {import('next').NextConfig} */
const nextConfig = {
  /* config options here */
}
 
module.exports = nextConfig

next.config.tsにNode.jsネイティブTypeScript解決器を使用する

注意:Node.js v22.10.0以上で、機能が有効になっている場合のみ利用可能です。Next.jsはそれを有効にしません。

Next.jsはNode.jsネイティブTypeScript解決器process.features.typescriptを介して検出します。これはv22.10.0で追加されました。存在する場合、next.config.tsはトップレベルのawaitとダイナミックimport()を含むネイティブESMを使用できます。このメカニズムはNode解決器の機能と制限を継承します。

Node.jsバージョンv22.18.0以上では、process.features.typescriptはデフォルトで有効になっています。v22.10.0から22.17.xの間のバージョンについては、NODE_OPTIONS=--experimental-transform-typesでオプトインします:

Terminal
NODE_OPTIONS=--experimental-transform-types next <command>

CommonJSプロジェクト(デフォルト)の場合

next.config.tsはCommonJSプロジェクトでネイティブESM構文をサポートしていますが、Node.jsはデフォルトではnext.config.tsをCommonJSファイルと想定しており、モジュール構文が検出されるとNode.jsはファイルをESMとして再解析します。したがって、CommonJSプロジェクトの場合はnext.config.mtsファイルの使用をお勧めします。これはESMモジュールであることを明示的に示します:

next.config.mts
import type { NextConfig } from 'next'
 
// トップレベルのawaitとダイナミックimportがサポートされています
const flags = await import('./flags.js').then((m) => m.default ?? m)
 
const nextConfig: NextConfig = {
  /* config options here */
  typedRoutes: Boolean(flags?.typedRoutes),
}
 
export default nextConfig

ESMプロジェクトの場合

package.json"type""module"に設定されている場合、プロジェクトはESMを使用しています。この設定について詳しくはNode.jsドキュメントをご覧ください。この場合、ESM構文を使用してnext.config.tsを直接書くことができます。

補足package.json"type": "module"を使用している場合、プロジェクト内のすべての.js.tsファイルはデフォルトでESMモジュールとして扱われます。必要に応じて、CommonJS構文を使用したファイルの名前を.cjsまたは.cts拡張子に変更する必要があります。

静的に型付けされたリンク

Next.jsはnext/linkを使用する場合、リンクを静的に型付けしてタイプミスなどのエラーを防ぎ、ページ間のナビゲーション時の型安全性を向上させることができます。

PagesルーターとApp Routerの両方でnext/linkhrefプロップで機能します。App Routerでは、next/navigationのメソッド(pushreplaceprefetchなど)も型付けされます。PagesRouterのnext/routerメソッドは型付けされません。

リテラルhref文字列は検証され、非リテラルhrefにはas Routeでのキャストが必要な場合があります。

この機能をオプトインするには、typedRoutesを有効にし、プロジェクトがTypeScriptを使用している必要があります。

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  typedRoutes: true,
}
 
export default nextConfig

Next.jsはアプリケーション内のすべての既存ルートに関する情報を含むリンク定義を.next/typesに生成し、TypeScriptはエディタで無効なリンクに関するフィードバックを提供するためにこれを使用できます。

補足create-next-appなしでプロジェクトをセットアップした場合は、生成されたNext.js型を含めることを確認してください。tsconfig.jsoninclude配列に.next/types/**/*.tsを追加してください:

tsconfig.json
{
  "include": [
    "next-env.d.ts",
    ".next/types/**/*.ts",
    "**/*.ts",
    "**/*.tsx"
  ],
  "exclude": ["node_modules"]
}

現在、サポートはダイナミックセグメントを含むあらゆる文字列リテラルに対応しています。非リテラル文字列については、as Routeで手動キャストが必要です。以下の例はnext/linknext/navigationの使用方法を示しています:

app/example-client.tsx
'use client'
 
import type { Route } from 'next'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
 
export default function Example() {
  const router = useRouter()
  const slug = 'nextjs'
 
  return (
    <>
      {/* Link: リテラルとダイナミック */}
      <Link href="/about" />
      <Link href={`/blog/${slug}`} />
      <Link href={('/blog/' + slug) as Route} />
      {/* hrefが有効なルートでない場合、TypeScriptエラーが発生します */}
      <Link href="/aboot" />
 
      {/* Router: リテラルとダイナミック文字列が検証されます */}
      <button onClick={() => router.push('/about')}>Push About</button>
      <button onClick={() => router.replace(`/blog/${slug}`)}>
        Replace Blog
      </button>
      <button onClick={() => router.prefetch('/contact')}>
        Prefetch Contact
      </button>
 
      {/* 非リテラル文字列の場合、Routeにキャストします */}
      <button onClick={() => router.push(('/blog/' + slug) as Route)}>
        Push Non-literal Blog
      </button>
    </>
  )
}

同じことはプロキシで定義されたリダイレクトルートに適用されます:

proxy.ts
import { NextRequest, NextResponse } from 'next/server'
 
export function proxy(request: NextRequest) {
  if (request.nextUrl.pathname === '/proxy-redirect') {
    return NextResponse.redirect(new URL('/', request.url))
  }
 
  return NextResponse.next()
}
app/some/page.tsx
import type { Route } from 'next'
 
export default function Page() {
  return <Link href={'/proxy-redirect' as Route}>Link Text</Link>
}

next/linkをラップするカスタムコンポーネントでhrefを受け入れるには、ジェネリックを使用します:

import type { Route } from 'next'
import Link from 'next/link'
 
function Card<T extends string>({ href }: { href: Route<T> | URL }) {
  return (
    <Link href={href}>
      <div>My Card</div>
    </Link>
  )
}

シンプルなデータ構造を型付けして反復処理し、リンクをレンダリングすることもできます:

components/nav-items.ts
import type { Route } from 'next'
 
type NavItem<T extends string = string> = {
  href: T
  label: string
}
 
export const navItems: NavItem<Route>[] = [
  { href: '/', label: 'Home' },
  { href: '/about', label: 'About' },
  { href: '/blog', label: 'Blog' },
]

次に、項目をマッピングしてLinkをレンダリングします:

components/nav.tsx
import Link from 'next/link'
import { navItems } from './nav-items'
 
export function Nav() {
  return (
    <nav>
      {navItems.map((item) => (
        <Link key={item.href} href={item.href}>
          {item.label}
        </Link>
      ))}
    </nav>
  )
}

どのように機能しますか?

next devまたはnext buildを実行するときは、Next.jsはアプリケーション内のすべての既存ルートに関する情報を含む隠しファイル.d.ts.next内に生成します(Linkhref型としてのすべての有効なルート)。この.d.tsファイルはtsconfig.jsonに含まれており、TypeScriptコンパイラはこの.d.tsをチェックし、エディタで無効なリンクに関するフィードバックを提供します。

環境変数のType IntelliSense

開発中、Next.jsはエディタのIntelliSenseのための読み込まれた環境変数に関する情報を含む.d.tsファイルを.next/typesに生成します。同じ環境変数キーが複数のファイルで定義されている場合、環境変数ロード順序に従って重複が排除されます。

この機能をオプトインするには、experimental.typedEnvを有効にし、プロジェクトがTypeScriptを使用している必要があります。

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  experimental: {
    typedEnv: true,
  },
}
 
export default nextConfig

補足:型は開発実行時に読み込まれた環境変数に基づいて生成され、デフォルトでは.env.production*ファイルからの変数が除外されます。本番環境固有の変数を含めるには、NODE_ENV=productionnext devを実行してください。

非同期Server Componentsの使用

TypeScriptでasync Server Componentを使用するには、TypeScript 5.1.3以上と@types/react 18.2.8以上を使用していることを確認してください。

古いバージョンのTypeScriptを使用している場合は、'Promise<Element>' is not a valid JSX element型エラーが表示される可能性があります。最新バージョンのTypeScriptと@types/reactに更新すると、この問題が解決されます。

インクリメンタル型チェック

Next.js v10.2.1以降、tsconfig.jsonで有効になっている場合、インクリメンタル型チェックをサポートしています。これは大規模なアプリケーションで型チェックの高速化に役立つ可能性があります。

カスタムtsconfigパス

場合によっては、ビルドまたはツール用に異なるTypeScript設定を使用したい場合があります。そのためには、next.config.tstypescript.tsconfigPathを設定して、Next.jsに別のtsconfigファイルをポイントさせます。

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  typescript: {
    tsconfigPath: 'tsconfig.build.json',
  },
}
 
export default nextConfig

例えば、本番ビルド用に別の設定に切り替えます:

next.config.ts
import type { NextConfig } from 'next'
 
const isProd = process.env.NODE_ENV === 'production'
 
const nextConfig: NextConfig = {
  typescript: {
    tsconfigPath: isProd ? 'tsconfig.build.json' : 'tsconfig.json',
  },
}
 
export default nextConfig
ビルド用に別のtsconfigを使用する理由

モノレポなどのシナリオで別の設定が必要な場合があります。ビルドはプロジェクトの標準に適合しない共有依存関係を検証したり、CIで厳密なTypeScript設定に移行するために検査を緩和してデリバリを続けたい場合(同時にIDEに誤用を強調してほしい場合)があります。

例えば、プロジェクトがuseUnknownInCatchVariablesを使用していても、モノレポ依存関係の一部がまだanyを想定している場合:

tsconfig.build.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "useUnknownInCatchVariables": false
  }
}

これにより、エディタはtsconfig.jsonを通じて厳密な状態に保ちながら、本番ビルドは緩和された設定を使用できます。

補足

  • IDEは通常、診断とIntelliSenseのためにtsconfig.jsonを読み取るため、本番ビルドが別の設定を使用する場合でもIDEの警告を表示できます。エディタでのパリティが必要な場合は、重要なオプションをミラーリングしてください。
  • 開発では、tsconfig.jsonのみ変更が監視されます。typescript.tsconfigPathで別のファイル名を編集する場合は、開発サーバーを再起動して変更を適用してください。
  • 設定されたファイルはnext devnext buildnext typegenで使用されます。

本番環境でのTypeScriptエラーの無効化

Next.jsは、プロジェクトにTypeScriptエラーが存在する場合、本番ビルドnext build)を失敗させます。

アプリケーションにエラーがあっても、Next.jsが危険な方法で本番コードを生成するようにしたい場合は、組み込み型チェックステップを無効にすることができます。

無効にした場合は、ビルドまたはデプロイプロセスの一部として型チェックを実行していることを確認してください。そうしないと、非常に危険な可能性があります。

next.config.tsを開き、typescript設定でignoreBuildErrorsオプションを有効にします:

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  typescript: {
    // !! 警告 !!
    // 危険:プロジェクトに型エラーがある場合でも、本番ビルドが正常に完了することを許可します。
    // !! 警告 !!
    ignoreBuildErrors: true,
  },
}
 
export default nextConfig

補足:ビルド前にTypeScriptエラーをチェックするためにtsc --noEmitを実行できます。これはデプロイ前にTypeScriptエラーをチェックしたいCI/CDパイプラインに便利です。

カスタム型宣言

カスタム型を宣言する必要がある場合、next-env.d.tsを修正しようとするかもしれません。ただし、このファイルは自動生成されるため、加えた変更は上書きされます。代わりに、new-types.d.tsと呼ぶ新しいファイルを作成し、それをtsconfig.jsonで参照する必要があります:

tsconfig.json
{
  "compilerOptions": {
    "skipLibCheck": true
    //...truncated...
  },
  "include": [
    "new-types.d.ts",
    "next-env.d.ts",
    ".next/types/**/*.ts",
    "**/*.ts",
    "**/*.tsx"
  ],
  "exclude": ["node_modules"]
}

バージョン変更

バージョン変更内容
v15.0.0TypeScriptプロジェクト向けのnext.config.tsサポートが追加されました。
v13.2.0静的に型付けされたリンクがベータで利用可能になりました。
v12.0.0SWCがデフォルトでTypeScriptとTSXをコンパイルするために使用されるようになり、ビルドが高速化されました。
v10.2.1tsconfig.jsonで有効になっている場合、インクリメンタル型チェックサポートが追加されました。