Next.jsを10.2へアップデートしたらビルドできなくなったので調査
ログを遡ってみると、以下のようなエラーが吐かれていた。
21:49:41.055 Failed to compile.
21:49:41.055 ./src/lib/blog.ts:56:33
21:49:41.055 Type error: Property 'draftKey' does not exist on type 'string | false | object'.
21:49:41.055 Property 'draftKey' does not exist on type 'string'.
21:49:41.055 54 |
21:49:41.055 55 | const id = toStringId(params.id);
21:49:41.055 > 56 | const draftKey = previewData?.draftKey
21:49:41.055 | ^
21:49:41.055 57 | ? { draftKey: previewData.draftKey }
21:49:41.055 58 | : {};
21:49:41.056 59 |
ビルドが失敗している原因はブログ記事を表示するロジックの部分だった。
ブログ記事を表示する際、それがプレビューモード によるものなのかどうかを、getStaticProps()
で受け取れる引数context
にpreviewData
が含まれているかどうかによって判定しているのだが、その部分で型エラーが発生していた。
今回ブログ投稿のロジックは一切触っていないはずなのにおかしいな…と思っていたら、Next.jsのgetStaticProps()
のcontext
の型定義が3週間前に変更されていた。
+ export type PreviewData = string | false | object | undefined
export type GetStaticPropsContext<Q extends ParsedUrlQuery = ParsedUrlQuery> = {
params?: Q
preview?: boolean
- previewData?: any
+ previewData?: PreviewData
locale?: string
locales?: string[]
defaultLocale?: string
}
getStaticProps()
で受け取れる引数context
の型は、上記のような感じで定義されている。
PreviewData
って、これまではany
型だったのか…知らなかった。
GetStaticPropsContext
のpreviewData
は、microCMSなどのヘッドレスCMSと連携してプレビューモード を実装する時に使っていて、これがany
からstring | false | object | undefined
に変わったことでコンパイルに失敗してしまっていた、ので対処する。
当初は、こんな感じでpreviewData
からdraftKey
を取得していた。
export const getPostData = async (
context: GetStaticPropsContext<ParsedUrlQuery>,
) => {
const { params, previewData } = context;
const draftKey = previewData?.draftKey
? { draftKey: previewData.draftKey }
: {};
...
GetStaticPropsContext
が持つpreviewData
がany
だった時はこれでも問題はなかったが、string | false | object | undefined
に変更された後だと、
プロパティ 'draftKey' は型 'string | false | object' に存在しません
というエラーが表示される。
んー?でもpreviewData
にobject
は含まれているし、optional chaining使っているし、previewData
をObject
と見做してうまく推論してくれてもいいのになーと思っていたが、調べてみると、TypeScriptのObject
とobject
は違うものであるらしい…
なので、最終的にType Guardを追加して解決した。
// 与えられた引数に`draftKey`値が存在するかチェックする
+ const isDraft = (item: any): item is { draftKey: string } =>
+ !!(item?.draftKey && typeof item.draftKey === 'string');
...
export const getPostData = async (
context: GetStaticPropsContext<ParsedUrlQuery>,
) => {
const { params, previewData } = context;
- const draftKey = previewData?.draftKey
+ const draftKey = isDraft(previewData)
? { draftKey: previewData.draftKey }
: {};
...
これでエラーが解消された。
反省点
- パッケージをアップデートしたら、デプロイ前にちゃんと動作確認する。
- パッケージの型定義には最低限目を通し、型チェックはなるべく厳密に行う。