Next.jsのTypeScriptテンプレートを作った

Next.jsをTypeScriptで使用するときのテンプレートを作りました。
nextjs-with-typescript-template

Reactを使った素振りでなにか作るときにNext.jsで始めるということが今後増えていきそうという理由で作った、よく使いそうなツールをまとめた自分用のテンプレートです。

型チェック

tscで型チェックを行います。
設定のベースは、create-next-appwith-typescriptテンプレートです。一部理解できていないオプションもあります。

  // tsconfig.json
  {
  	  "compilerOptions": {
  	    "allowJs": true,
  	    "alwaysStrict": true,
  	    "esModuleInterop": true,
  	    "forceConsistentCasingInFileNames": true,
  	    "isolatedModules": true,
  	    "jsx": "preserve",
  	    "lib": ["dom", "es2017"],
  	    "module": "esnext",
  	    "moduleResolution": "node",
	    "noEmit": true,
	    "noFallthroughCasesInSwitch": true,
	    "noUnusedLocals": true,
	    "noUnusedParameters": true,
	    "resolveJsonModule": true,
	    "skipLibCheck": true,
	    "strict": true,
	    "target": "esnext"
	  },
	  "exclude": ["node_modules"],
	  "include": ["**/*.ts", "**/*.tsx"]
}

上記のオプションについて

allowJs

ts, tsxファイル以外のimportの許容。


alwaysStrict

各ファイルへ"use strict"をつけて出力することを保証する。出力されたJSファイルはECMAScriptのStrictモードで実行される。
Strictモードについては以下を参照。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Strict_mode


forceConsistentCasingInFileNames

ファイル名の大文字小文字を区別する。


jsx

jsx構文がどのようにファイルに出力されるかを制御するオプション。preserve, react, react-nativeがありpreserveではJSXを変更することなく.jsxファイルを出力する。


lib

コンパイルする際に使用する組み込みのJS API型定義や、ブラウザ上で使用されるAPIの型定義のライブラリを指定する。


module

出力されるJSのモジュールの形式を指定する。CommonJS形式(exports.*)かESModules(export ***)かを指定。


noEmit

JavaScript ソースコード、ソースマップ、型定義のファイルを出力しないようにします。型チェック機能だけを使用する場合にtrueにする。Next.jsはtscを使わずに内部でBabelでコンパイルしているらしい。


noFallthroughCasesInSwitch

switch 文において、次の case へ処理を持ち越した場合にエラーを報告する。 switch 文内の空でない case 句が、breakまたはreturnを含むことを確約する。


noUnusedLocals 

利用されていないローカル変数について、エラーを報告する。


noUnusedParameters

利用されていない関数のパラメータについて、エラーを報告する。


resolveJsonModule

.json拡張子のファイルをモジュールとしてimportできるようにする。


skipLibCheck

型定義ファイルのチェックをスキップすることで、コンパイル実行時間を短縮する。


strict

プログラムの正しさを強く保証するための幅広い型チェックの挙動を有効化する。strictBindCallApply, strictFunctionTypes, strictNullChecks, strictPropertyInitializationオプションの有効化と等価。


target

出力されるJSのバージョンの指定。

理解できていないオプション

  • isolatedModules
  • moduleResolution
  • esModuleInterop


linter, formatter

ESLint + Prettierの構成です。

// .eslintrc.json
{
	  "parser": "@typescript-eslint/parser",
	  "extends": [
	    "plugin:@typescript-eslint/recommended",
	    "plugin:react/recommended",
	    "plugin:prettier/recommended",
	    "plugin:prettier/react",
	    "plugin:prettier/@typescript-eslint"
	  ],
	  "plugins": ["@typescript-eslint", "react", "prettier"],
	  "rules": {
	    "react/react-in-jsx-scope": "off"
	  },
	  "globals": {
	    "React": "writable"
	  }
}

上記の設定について

parser

ESLintがTypeScriptをパースできるようにするためのパーサの指定。


extends

pluginによって提供されるルールを使用することによって設定を拡張するという理解。「plugin:{プラグイン名}/{設定名}」という形で指定する。

  • plugin:@typescript-eslint/recommended

TypeScript用のESLintルール

  • plugin:react/recommended

React用のESLintルール

  • plugin:prettier/recommended

ESLintとPrettierを共存させるためのルール。ESLintのフォーマットに関する設定を全てOFFにし、フォーマットに関してはESLintの中からPrettierにすべて任せる

  • plugin:prettier/react

eslint-plugin-reactのフォーマットに関するルールをOFFにする。

  • plugin:prettier/@typescript-eslint

競合するESLintのルールを無効化する。最後に書く必要があるっぽい。


plugins

eslint-plugin-xxxのxxxの部分を指定する(eslint-plugin-は省略可能なため)。xxxに関する設定の塊みたいな認識。

  • @typescript-eslint
  • react
  • prettier


rules

特定のルール(最小単位)のON/OFFを切り替える。

  • react/react-in-jsx-scope

Next.jsでは各jsx/tsxファイル内でReactをimportする必要がないので、OFFにする。


globals

実行時に追加するグローバル変数の追加の指定。writableを指定することで変数に対しての書き込みも可能になる。

  • React

Next.jsでは各jsx/tsxファイルでReactをimportしないため。


フォーマットに関する設定は.prettierrcに書く。

// .prettierrc
{
	  "semi": true,
	  "singleQuote": true
}


テスト

jestでユニットテストを実行します。

// jest.config.js
module.exports = {
	  roots: ['<rootDir>/src'],
	  testMatch: [
	    '**/__tests__/**/*.+(ts|tsx|js)',
	    '**/?(*.)+(spec|test).+(ts|tsx|js)',
	  ],
	  transform: {
	    '^.+\\.(ts|tsx)$': 'ts-jest',
	  },
	};


今回作ったテンプレートでは「tests」というディレクトリを作って、その中に「xxx.test.ts」のようなファイルを作ってyarn testでテストを実行するようにしています。

precommit

huskyを使用して、precommitのhookで型チェック・lint・テストを回しています。
規約を破るソースコードはpushさせないという思想です。

テンプレートを作ってみた感想

  • 設定が多い。とはいえ、Next.jsがBabelやwebpackの設定を隠蔽してくれているので楽な方なんだと思う。
  • ESLintは特に複雑に感じる。ただ、今回テンプレートを作ってみたことでESLintへの理解度は上がった。
  • 大変だけど、最初にこのへんの設定をセットアップしておけば後から設定の中身を変更することは大変じゃないのでテンプレートを作った価値はあった。特にリントツールは初期に導入を済ませておくことに価値があるはず。
  • このテンプレートを使ってNext.jsの素振り頑張る。