🤔

Next.js は React の型情報をどのように拡張しているのか

2024/10/08に公開

はじめに

Next.js 14 では React 19 で搭載予定の新機能がすでにサポートされており React 19 でなくとも利用することができます。
たとえば、useTransition フックは新しく非同期関数も扱えるようになりました。使い方はこちらの記事にて紹介しています。

https://zenn.dev/k0rosuke/articles/eab514e4920778#usetransition-フックを利用して処理中状態を管理する

この新機能自体は Next.js がサポートしてるので正常に動作するのはわかるのですが、型情報はどうでしょうか?
useTransition フック自体は react パッケージからインポートして利用するので React 19 でなければ新しい型情報は含まれていないように思えます。が、実際に試してみると Next.js と一緒に利用する場合は新しい型情報に対応しているのです。


react@18.2.0(@types/react@18.2.0) の場合、非同期関数に対応してないので型エラーになる


react@18.2.0(@types/react@18.2.0) with Next.js 14の場合、なぜか型エラーにならない

本記事では Next.js が react パッケージの型情報をどのように拡張しているのかについて説明します。

TL;DR

最初に結論です。

  • @types/react@^18.2.0 パッケージにはすでに React 19 で搭載予定の新機能に対応した新しい型情報に拡張するための型定義ファイルが用意されている
  • next@14 は新しい型情報に拡張するための型定義ファイルを読み込み適用させている

next@14 パッケージは互換性のある react パッケージのバージョン範囲を ^18.2.0 としています[1]@types/react パッケージもライブラリ本体とバージョンを揃える必要があるため、^18.2.0 となります[2]

詳細

@types/react@^18.2.0 パッケージにはすでに React 19 で搭載予定の新機能に対応した新しい型情報に拡張するための型定義ファイルが用意されている

react パッケージの型情報は、@types/react パッケージから配布されています。import {} from "react"; とすると、TypeScriptのモジュール解決により node_modules/@types/react/index.d.ts 型定義ファイルの型情報を参照します[3]。この型定義ファイルには React 19 の新しい型情報は含まれていません。
ではどのファイルが新しい型情報を提供しているのでしょうか?実際に Next.js のプロジェクトを用意し、コードエディタから型定義ファイルへ飛んで確かめてみるとします。

すると、node_modules/@types/react/canary.d.ts 型定義ファイルへジャンプしました。この型定義ファイルを読み込むと新しい型情報に拡張されるようです。ファイル先頭のコメント行を見ると親切にもこの型情報を適用させるための3つの方法が記載されています。このことから、きっと Next.js はこの3つの方法のどれかで型情報を適用させてるはずと予想できます。

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/154a9c00c398697d7b889deb55842eaafc8b44c7/types/react/canary.d.ts#L1-L22

next@14 は新しい型情報に拡張するための型定義ファイルを読み込み適用させている

用意した Next.js のプロジェクトで先程の canary.d.ts を適用させているコードがないか検索をかけてみます。プロジェクト内のコードではヒットしなかったので、やはり node_modules が怪しそうです。
Next.js は next パッケージでライブラリ本体とその型情報も配布しているので node_modules/next ディレクトリに検索をかけてみます。が、らしきコードは見つかりません。
探索範囲を広げて node_modules 内で試行錯誤しながら検索を続けていると node_modules/@types/react/experimental.d.ts 型定義ファイル内で canary.d.ts を読み込むコードを見つけました。

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/154a9c00c398697d7b889deb55842eaafc8b44c7/types/react/experimental.d.ts#L37

どうやら @types/react パッケージは canary.d.ts による型拡張に加え、さらに experimental.d.ts による型拡張もできるようです。バージョニングポリシーによると React は、Latest チャンネルCanary チャンネルExperimental チャンネルの3つのリリースチャンネルを提供しており、型定義ファイルはそれぞれのリリースチャンネルに対応している感じでしょうか。
話はそれましたが、試しに上記import文をコメントアウトしてみると、canary.d.ts で提供されている新しい型情報を参照できなくなりました。experimental.d.ts 型定義ファイルがどこかで読み込まれているのは間違いなさそうです。
こちらもファイル先頭のコメント行に型情報を適用させるための3つの方法が記載されています。

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/154a9c00c398697d7b889deb55842eaafc8b44c7/types/react/experimental.d.ts#L1-L25

experimental.d.ts を適用させているコードがないかnode_modules/next ディレクトリに再び検索をかけてみます。 すると node_modules/next/types/index.d.ts 型定義ファイル内で該当箇所を見つけることができました。ヨカッタ

https://github.com/vercel/next.js/blob/v14.0.0/packages/next/types/index.d.ts#L3

プロジェクトのどこかで import {} from "next"; とすると上記ファイルが読み込まれ react パッケージの型情報が拡張されます。

まとめ

いかがだったでしょうか?
今回は Next.js 14 が React の型情報をどのように拡張しているのかについて調査してみました。

参考になれば幸いです。

脚注
  1. パッケージの互換性は package.jsonpeerDependencies で指定できます。実際の指定箇所は https://github.com/vercel/next.js/blob/v14.0.0/packages/next/package.json#L105 を参照 ↩︎

  2. @types のバージョンと対応するライブラリ本体のバージョンの関係性はこちらを参考ください ↩︎

  3. TypeScriptのモジュール解決についてはこちらの記事がわかりやすいです ↩︎

Discussion