はじめに
- 前回でGatsbyの環境構築を行った
- 今回はTypeScriptの恩恵を受けられるようにESLint, Prettierの設定を行う
- GatsbyをTypeScript化してESLintとPrettierを導入するが非常に参考になった
- 普段はVSCodeを利用しているため、VSCodeの拡張機能も設定する
- 完成形はこちら
TypeScriptの導入
GatsbyにはデフォルトでTypeScriptに対応しているが、型チェックの恩恵を十分に得られないため、DX(Developer Experience)を向上させるために追加の設定を行っていく。
tsconfig.jsonの作成
{
"include": ["./src/**/*"], // コンパイル対象
"compilerOptions": {
"target": "esnext", // 出力するjsのバージョン
"module": "esnext", // 出力するjsのモジュール
"lib": ["dom", "es2017"], // コンパイルする際に使用する組み込みライブラリ
"jsx": "react", // tsxファイルの出力形式
"esModuleInterop": true, // CommonJSモジュールのインポートをサポートする
"forceConsistentCasingInFileNames": true, // 大文字小文字を区別して参照解決する
"strict": true, // 型チェックをstrictモードにする
"skipLibCheck": true, // 型定義ファイルの整合性チェックを行わない
"noEmit": true, // コンパイル結果を出力しない
"moduleResolution": "node"// tscのモジュール解決方法(新しい環境で行う場合はnodeを指定する)
}
}
.js -> .tsxへ変更する
- src/下の.jsファイルを.tsxに変更する
- この状態で
yarn develop
をしても問題なく動作するが静的解析の恩恵は得たい
ESLintの設定
- JS のための静的検証ツール
- Pythonのflake8みたいなやつ
yarn add -D eslint eslint-loader
yarn run eslint --init
# 以下、選択内容
? How would you like to use ESLint? …
To check syntax only
To check syntax and find problems
❯ To check syntax, find problems, and enforce code style
? What type of modules does your project use? …
❯ JavaScript modules (import/export)
CommonJS (require/exports)
None of these
? Which framework does your project use? …
❯ React
Vue.js
None of these
? Does your project use TypeScript? › No / [Yes]
? Where does your code run? … (Press <space> to select, <a> to toggle all, <i> to invert selection)
✔ [Browser]
✔ Node
? How would you like to define a style for your project? …
❯ Use a popular style guide
Answer questions about your style
Inspect your JavaScript file(s)
? Which style guide do you want to follow? …
❯ Airbnb: https://github.com/airbnb/javascript
Standard: https://github.com/standard/standard
Google: https://github.com/google/eslint-config-google
XO: https://github.com/xojs/eslint-config-xo
? What format do you want your config file to be in? …
JavaScript
YAML
❯ JSON
? Would you like to install them now with npm? › No / [Yes]
- この状態で
yarn run eslint --fix "**/*.tsx"
を実行すると大量のエラー文が出力される- .cache/, public/もESLintの対象になっているが後述のtsconfig.eslint.jsonで対象外にする
ESLintで出るエラーの解消するために.eslintrc.jsonを修正する
{
"extends": [
// 省略
"plugin:import/errors", // 追加
"plugin:import/warnings", // 追加
"plugin:import/typescript", // 追加
],
"rules": {
"no-use-before-define": "off", // 追加
"@typescript-eslint/no-use-before-define": ["error"], // 追加
"react/jsx-filename-extension": ["error", { "extensions": [".jsx", ".tsx"] }], // 追加
"react/no-unescaped-entities": "off", // 追加
"react/prop-types": "off" , // 追加
"react/require-default-props": "off", // 追加
"import/extensions": [ // 追加
"error",
"ignorePackages",
{
"js": "never",
"jsx": "never",
"ts": "never",
"tsx": "never"
}
]
},
}
-
この状態で
yarn run eslint --fix "**/*.tsx"
を実行するとエラーは全て消えているが、型が指定されていなかったり、そもそもインストールされていなかったりする(react-helmetなど) -
これをチェックするため、eslintrc.jsonにさらに以下を追加すると型が指定されていないエラーなどが出現する
"extends": [
// 省略
"plugin:@typescript-eslint/recommended", // 追加
"plugin:@typescript-eslint/recommended-requiring-type-checking", // 追加
],
"parserOptions": {
// 省略
"project": "./tsconfig.eslint.json" // 追加
},
- parserOptions.projectに設定したtsconfig.eslint.jsonでESLint対象のファイルを設定する
{
"extends": "./tsconfig.json",
"include": ["./src/**/*"],
}
TypeScirptへの本格対応
- TypeScriptの型チェックが正しく効くようになったため、ソースコードを修正していく
src/componentsの修正
- src/components/seo.tsxを例にとる(他のcomponentsも同様に修正)
- 修正ポイントは以下の通り
- react-helmetの型定義
- Seo関数の型定義
- GraphQLの型定義
react-helmetの型定義
import { Helmet } from "react-helmet";
- react-helmetの定義ファイルがないために、VSCodeがエラーを吐いている
- これは定義ファイルをインストールすれば解決
yarn add -D @types/react-helmet
Seo関数の型定義
- Seo関数の型定義がされていないため、propsとSeoの型を定義する
- Seo.defaultProps, Seo.propTypesは不要のため削除
- 参考にした記事
type SEOProps = {
title: string;
lang?: string;
meta?: React.ComponentPropsWithoutRef<'meta'>[];
description?: string;
};
const SEO: React.VFC<SEOProps> = ({
title,
lang = 'ja',
meta = [],
description = '',
}) => {
// 省略
}
GraphQLの型定義
- GraphQLで得られたレスポンスの型をgatsby-plugin-typegenで生成する
yarn add gatsby-plugin-typegen
- gatsby-config.jsにpluginを追加
plugins: [
// 省略
`gatsby-plugin-typegen`,
]
-
yarn build
を実行し、src/__generated__
ディレクトリが作成されることを確認する - これでエラーは消える(消えない場合は
Ctrl + Shift + P
などでVSCodeのWindowを再起動してみる)
src/pagesの修正
- src/pages/index.tsxを例にとる(他のpagesも同様に修正)
- IndexPage関数の型定義を加えればOK
const IndexPage: React.VFC<PageProps> = () => (
// 省略
)
Prettierの設定
- JSのためのコードフォーマッタ
- Pythonのblackみたいなやつ
- prettierはデフォルトでインストールされている
- 設定ファイル.prettierrcを修正する
- Prettier
.prettierrc
{
"arrowParens": "avoid",
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5"
}
ESLintとPrettierの競合回避
yarn add -D eslint-config-prettier
- eslintrc.jsonのextendsにprettierを追加
"extends": [
// 省略
"prettier"
],
- この状態で
yarn run prettier --write "**/*.tsx"
を実行するとコードフォーマットが実行される
パッケージ追加 & package.jsonの設定
- ESLint, Prettierを実行したい時に長ったらしいコマンドを打つのは面倒
- コミットする前にELint, Prettierが確実に実行されるようにしてコードの品質を保ちたい
ESLint, Prettierの実行を楽にする
- package.jsonにスクリプトを登録しておく
"scripts": {
// 省略
"format:prettier": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
"format:eslint": "eslint --fix",
"format": "yarn format:eslint && yarn format:prettier"
}
- これで
yarn format
を実行すればESLintもPrettierも実行される
(Optional) コミット前にESLint, Prettierが実行されるようにする
- ※現状ESLintでエラーが吐かれている状態のため、以下の設定をすると解消するまでコミットができなくなるので注意
- 必要なパッケージをインストールする
- husky v6 のインストール方法と使い方。lint-staged も導入して、品質を保とうが参考になった!
- yarnのバージョンが異なると手順が異なるようなので、詳細は↑を参照
# yarn v1の場合
yarn add -D husky lint-staged
yarn husky install
yarn husky add .husky/pre-commit "yarn lint-staged"
- Package.jsonに設定を記述しておく
"scripts: {
// 省略
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write \"**/*.{js,jsx,ts,tsx}\"",
],
"*.{json,md}": [
"prettier --write \"**/*.{json,md}\""
]
},
VSCodeの設定
- VSCodeでリアルタイムコード解析がされるようにする
- VSCodeで保存時に自動でESLint, Prettierを自動実行してくれるようにする
拡張機能のインストール
以下の拡張機能をインストールするとリアルタイムコード解析がされるようになる
- ESLint: dbaeumer.vscode-eslint
- Prettier - Code formatter: esbenp.prettier-vscode
保存時にESLint, Prettierを自動実行するように設定する
-
※以下で行う設定を有効化するには開いているWorkspase直下に後述する.vscodeが存在している必要があるので注意
-
プロジェクトルート直下に.vscodeフォルダを作成し、.vscode直下に以下のsettings.jsonを作成する
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true
},
}
- 蛇足だが.vscode直下にextensions.jsonを作成しておくと推奨拡張機能などを記述できるため、チーム開発等に便利
- 詳しくは公式ドキュメントを参照
- VSCodeのコード解析をすると、赤線が引かれているところが散見されるため次項で対応する
tscの対応
- TypeScriptによるコンパイルチェックとESLintの静的解析は内容が異なる
- 具体例:
Object is possibly 'undefined'.ts(2532)
- 参考: ESLint not reporting TypeScript compiler type checking errors
- 具体例:
- まずはこれを
yarn format
で検出できるようにpackage.jsonを修正しておく-
lint-staged
と組み合わせるとtsc
がうまく動作せずerror TS5042: Option 'project' cannot be mixed with source files on a command line.
などのエラーが発生してしまう - そこでbashをかませることでエラーを回避する
- 参考: lint-staged ignores tsconfig.json when it called through husky hooks
-
"scripts": {
// 省略
"check-types": "tsc --noemit", // 追加
"format:prettier": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
"format:eslint": "eslint --fix",
"format": "yarn format:eslint && yarn format:prettier && yarn check-types", // 修正
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write \"**/*.{js,jsx,ts,tsx}\"",
"bash -c tsc --noEmit" // 追加
],
},
-
次にエラーが出ないように修正していく
- 参考: 【Typescript】Nullableに対する様々な操作
- またsrc/components/seo.tsxを例にとる(他のcomponentsも同様に修正)
-
GraphQLのレスポンスとして
undefined
かもしれないオブジェクトは?をつけてNull許容型にする
const metaDescription = description || site?.siteMetadata?.description;
- titleTemplateの型を合わせるためにnullからundefinedに変更する
<Helmet
htmlAttributes={{
lang,
}}
title={title}
titleTemplate={defaultTitle ? `%s | ${defaultTitle}` : undefined} // null -> undefined
meta={defaultMeta.concat(meta)} // defaultMetaは後述
/>
- metaの型を明示する
const defaultMeta: React.ComponentPropsWithoutRef<"meta">[] = [
{
name: "description",
content: metaDescription,
},
// 省略
]