create-next-appで作成したNext.jsアプリケーションをTypeScriptで動くようにする

Next.jsのアプリケーションを簡単に作るcreate-next-appというものを知ったので、これで作ったものをTypeScriptで動くようにしてみました。

セットアップ

create-next-appでさっと作っておきましょう。

$ npx create-next-app my-first-next-app
$ cd my-first-next-app

TypeScriptセットアップ

tsconfig.jsonを配置します。

$ touch tsconfig.json

不足しているライブラリを指摘してくるので、コピペして追加しましょう。

$ yarn dev
[ wait ]  starting the development server ...
[ info ]  waiting on https://localhost:3000 ...
It looks like you're trying to use TypeScript but do not have the required package(s) installed.

Please install typescript, @types/react, and @types/node by running:

    yarn add --dev typescript @types/react @types/node

If you are not trying to use TypeScript, please remove the tsconfig.json file from your package root (and any TypeScript files).

error Command failed with exit code 1.

$ yarn add --dev typescript @types/react @types/node

もう一度yarn devを実行すると、tsconfig.jsonの中身が生成されます。

$ yarn dev
yarn run v1.16.0
$ next dev
[ wait ]  starting the development server ...
[ info ]  waiting on https://localhost:3000 ...
We detected TypeScript in your project and created a tsconfig.json file for you.

Your tsconfig.json has been populated with default values.

[ info ]  bundled successfully, waiting for typecheck results...

作成されたtsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve"
  },
  "exclude": [
    "node_modules"
  ],
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx"
  ]
}

js -> tsx

create-next-appで作成した場合、pages/index.jscomponents/nav.jsの2ファイルでReactのJSXが記述されています。これを拡張子tsxに変えましょう。

変更すると、TypeScriptのエラーが出ます。

[ error ] ERROR in /Users/develop/site/components/nav.tsx(8,8):
8:8 Property 'key' does not exist on type '{ href: string; label: string; }'.
     6 |   { href: 'https://github.com/zeit/next.js', label: 'GitHub' },
     7 | ].map(link => {
  >  8 |   link.key = `nav-link-${link.href}-${link.label}`
       |        ^
     9 |   return link
    10 | })
    11 | 
undefined
undefined

nav.tsxlinksの型に問題がある様子です。

TypeScriptのコンパイルエラーを解決する

linksは以下のような定義がされています。

const links = [
  { href: 'https://zeit.co/now', label: 'ZEIT' },
  { href: 'https://github.com/zeit/next.js', label: 'GitHub' },
].map(link => {
  link.key = `nav-link-${link.href}-${link.label}`
  return link
})

ここで問題になっているのは、keyプロパティです。はじめの定義ではhreflabelの2プロパティしかありません。そのために型推論ではこの2つだけを持つ配列を判断されています。そこに後続のmapで未定義のkeyプロパティを追加しようとしているためにエラーがでています。

これを解決するシンプルな方法は、プロパティの追加方法を変更することです。

const links = [
  { href: 'https://zeit.co/now', label: 'ZEIT' },
  { href: 'https://github.com/zeit/next.js', label: 'GitHub' },
].map(link => {
  return {
    ...link,
    key: `nav-link-${link.href}-${link.label}`
  }
})

型推論がすでに行われているlinkに直接追加するのではなく、新しいオブジェクトとしてreturnさせました。これでlinkshref, label, keyの3プロパティをもつ配列と推論されるようになります。

おわりに

ボイラープレートをJavaScriptからTypeScriptに変換しただけの簡単なサンプルでしたので、手数はかなり少なめでした。

tsconfig.jsonを自動生成してくれるのは個人的には好感をもてます。ブラックボックスにされていない点も含めて。

Comment