💯

[Next] getStaticPropsの型の付け方、型定義について

2021/04/24に公開

Next.jsを学習していて、getStaticPropsにどうやって型をつけるかがわからなかったので調べてみました。
公式にも情報はあったのですが、具体的に引数に何を渡すかなどの情報が書いていなかったのが動機です。
まずはどう使うのか載せて、その後どうやって型定義を調べたかまとめます。

getStaticPropsの型の付け方

コメントに実装手順を書いてます。

import { getItem, getItems } from 'src/lib/items'
import { GetStaticProps, GetStaticPaths } from 'next'
import { ParsedUrlQuery } from 'node:querystring'

export interface Item {
  userId: number
  id: number
  title: string
  body: string
}

interface Props {
  item: Item
}

// 1. Paramsの型を定義し、ParsedUrlQueryをextendsする
interface Params extends ParsedUrlQuery {
  id: string
}

// 3. VFCにもPropsを渡す
const Items: React.VFC<Props> = ({ item }) => {
  return <div>{ item }</div>
}

// 2. GetStaticPropsにPropsとParamsを渡す
export const getStaticProps: GetStaticProps<Props, Params> = async ({
  params,
}) => {
  const item = await getItem(params!.id)

  return {
    props: {
      item,
    },
  }
}

export const getStaticPaths: GetStaticPaths<Params> = async () => {
  const items = await getItems()
  const paths = items.map(({ id }) => ({
    params: {
      id: id.toString(),
    },
  }))

  return {
    paths,
    fallback: false,
  }
}

export default Items

GetStaticPropsの定義

GetStaticPropsの定義をindex.d.tsで確認します。

export type GetStaticProps<
  P extends { [key: string]: any } = { [key: string]: any },
  Q extends ParsedUrlQuery = ParsedUrlQuery
> = (context: GetStaticPropsContext<Q>) => Promise<GetStaticPropsResult<P>>

上記よりGetStaticProps<P, Q>として使用することができそうです。
Pはオブジェクト型、QはParsedUrlQueryを継承した型っぽいですね。

またgetStaticPropsは返り値としてGetStaticPropsResult<P>(のPromise)を返すようです。
GetStaticPropsResultの引数にはGetStaticPropsの引数Pが渡されています。

引数としてはGetStaticPropsContext<Q>(context)を受け取るようです。
GetStaticPropsContextの引数にはGetStaticPropsの引数Qが渡されています。

なのでこれら2つの型の定義を確認してみます。
まずは返り値から見ます。

GetStaticPropsResult<P>の定義

export type GetStaticPropsResult<P> =
  | { props: P; revalidate?: number | boolean }
  | { redirect: Redirect; revalidate?: number | boolean }
  | { notFound: true }

上記より、getStaticPropsの返り値はprops, redirect, notFoundのいずれかを含むオブジェクトであることがわかりました。
確かに、getStaticPropsでは

export const getStaticProps = () => {
  return {
    props: {
      item
    }
  }
}

のようにpropsを含んだオブジェクトを返します。

そしてGetStaticPropsの引数Pは、返り値のpropsの型を定義するのに使われています。
つまり、propsの型を指定するためにGetStaticPropsに引数Pを渡していることになります。

続いて引数について見てみます。

GetStaticPropsContext<Q>の定義

export type GetStaticPropsContext<Q extends ParsedUrlQuery = ParsedUrlQuery> = {
  params?: Q
  preview?: boolean
  previewData?: any
  locale?: string
  locales?: string[]
  defaultLocale?: string
}

上記より、getStaticPropsの引数contextにはparams, preview, previewData, locale, locales, defaultLocaleのいずれかを含むことがわかりました。
?がついているので、含めなくても問題ありません。オプションです。

確かに、getStaticPropsでは

export const getStaticProps = (context) => {
  const id = context.params.id 

  return {
    props: {
      item
    }
  }
}

export const getStatisPaths = () => {
  const paths = [
    { params: { id: 1 } },
    { params: { id: 2 } },
    ...
  ]

  return {
    paths,
    fallback: false,
  }
}

のように、getStaticPathsでreturnしたpaths内のオブジェクトを引数contextから参照します。

そしてGetStaticPropsの引数Qは、引数contextのparamsの型を定義するのに使われています。
つまり、context.paramsの型を指定するためにGetStaticPropsに引数Qを渡していることになります。

まとめ

getStaticPropsの型の付け方、GetStaticProps<P, Q>の型定義を確認しました。

P -> getStaticPropsの返り値の型
Q -> getStaticPropsの引数contextの内部の型

を定義していることがわかりました。


P.S.
getStaticPathsも同じ要領で調べることができます。
最初に載せた例に使い方書きましたので参考にしてみてください。

Discussion