コンテンツにスキップ

TypeScript

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

既存のプロジェクトに TypeScript を追加するには、ファイルを .ts / .tsx にリネームします。next dev および next 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 はデフォルトでサーバーコンポーネントを使用するため、DateMapSet などを追加の手順なしに使用できます。以前は、Next.js 固有の型でサーバーとクライアントの境界を手動で型付けする必要がありました。
  2. コンポーネント間のデータフローの合理化: _app をルートレイアウトに置き換えたことで、コンポーネントとページ間のデータフローを視覚化しやすくなりました。以前は、個々の pages_app 間のデータフローは型付けが難しく、混乱を招くバグの原因となる可能性がありました。App Router での共配置されたデータフェッチにより、これはもはや問題ではありません。

Next.js でのデータフェッチは、データベースまたはコンテンツプロバイダーの選択に限定されず、可能な限りエンドツーエンドの型安全性を実現します。

期待どおりに、通常の TypeScript を使用してレスポンスデータを型付けできます。たとえば、

app/page.tsx
async function getData() {
  const res = await fetch('https://api.example.com/...')
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.
  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 を使用して、Next.js 設定で TypeScript と型のインポートを使用できます。

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

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

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 でオプトインしてください。

ターミナル
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'
 
// Top-level await and dynamic import are supported
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 ドキュメント を参照してください。この場合、next.config.ts を ESM 構文で直接記述できます。

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

Next.js は、next/link を使用する際にタイポやその他のエラーを防ぐために、リンクを静的に型付けすることができます。これにより、ページ間のナビゲーション時の型安全性が向上します。

Pages Router と App Router の両方で、next/linkhref プロパティに対して機能します。App Router では、pushreplaceprefetch などの next/navigation メソッドも型付けされます。Pages Router の 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 なしでセットアップした場合、tsconfig.jsoninclude 配列に .next/types/**/*.ts を追加して、生成された Next.js の型が含まれるようにしてください。

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: literal and dynamic */}
      <Link href="/about" />
      <Link href={`/blog/${slug}`} />
      <Link href={('/blog/' + slug) as Route} />
      {/* TypeScript error if href is not a valid route */}
      <Link href="/aboot" />
 
      {/* Router: literal and dynamic strings are validated */}
      <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>
 
      {/* For non-literal strings, cast to 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 は .next 内に非表示の .d.ts ファイルを生成します。このファイルには、アプリケーション内のすべての既存のルート(Linkhref 型としてのすべての有効なルート)に関する情報が含まれています。この .d.ts ファイルは tsconfig.json に含まれており、TypeScript コンパイラは .d.ts をチェックし、エディターで無効なリンクに関するフィードバックを提供します。

環境変数用の型イントロスペクション

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

この機能を利用するには、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 を実行します。

非同期サーバーコンポーネント付き

TypeScript 5.1.3 以降、および @types/react 18.2.8 以降を使用していることを確認してください。

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

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

v10.2.1 以降、Next.js は インクリメンタル型チェック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
  }
}

これにより、IDE は tsconfig.json を通じて厳格に保たれ、本番ビルドは代替設定を使用できます。

知っておくと良いこと:

  • 通常、IDE は診断とイントロスペクションのために tsconfig.json を読み取ります。そのため、本番ビルドが代替設定を使用している間でも、IDE の警告を確認できます。エディターでの整合性を確保するには、重要なオプションをミラーリングしてください。
  • 開発中、tsconfig.json のみが変更を監視されます。typescript.tsconfigPath を介して別のファイル名を編集した場合、設定を適用するには開発サーバーを再起動してください。
  • 設定されたファイルは、next devnext build、および next 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: {
    // !! WARN !!
    // Dangerously allow production builds to successfully complete even if
    // your project has type errors.
    // !! WARN !!
    ignoreBuildErrors: true,
  },
}
 
export default nextConfig

知っておくと良いこと: ビルド前に tsc --noEmit を実行して TypeScript エラーを自分でチェックできます。これは CI/CD パイプラインで、デプロイ前に TypeScript エラーをチェックしたい場合に役立ちます。

カスタム型宣言

カスタム型を宣言する必要がある場合、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.0next.config.ts で TypeScript プロジェクトのサポートが追加されました。
v13.2.0静的に型付けされたリンクがベータ版で利用可能になりました。
v12.0.0TypeScript および TSX のコンパイルには、デフォルトで SWC が使用され、ビルドが高速化されます。
v10.2.1tsconfig.json で有効にした場合の インクリメンタル型チェック のサポートが追加されました。