📻

サバイバルTypeScriptメモ【ESlint編】

2022/05/21に公開約14,000字

# 概要

サバイバルTypeScriptのメモです。
今回の記事では、サバイバルTypeScriptのESlintの章をまとめていきます。

個人的には、学んだことを後から振り返ってわかるようにまとめる「ノート」のような感覚で記載しています。
その分読みにくいところなどあるかと思いますが、ご了承ください。🙇‍♂️

【補足】
この記事のハンズオンの部分は、専用のリポジトリを作成しGitHubにあげています。
専用のリポジトリ

# この章の目的(ゴール)

※これはあくまで、サバイバルTypeScriptの目的(ゴール)です。
TypeScriptをESlintでチェックできるようになること。

# ESLintの設定ファイルを記述

ESLintには、lintチェックの際のオプションなどを設定するための設定ファイル(.eslintrc.jsファイルなど)が必要になる。

【サンプルファイル】

module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true,
  },
  parserOptions: {
    ecmaVersion: "latest",
    sourceType: "module",
  },
};

### 上記のサンプルファイルのそれぞれのオプションについて

# root

eslintコマンドを実行したディレクトリを起点に、ディレクトリを遡って、設定ファイルという仕様がeslintには存在します。
例えば、ディレクトリ/a/b/eslintコマンドを実行したとします。その場合、ESLintは次の順番で設定ファイルを探し出します。
/a/b/.eslintrc.js
/a/.eslintrc.js
/.eslintrc.js
この探索は、ルートディレクトリまで遡ります。
そして、この探索中に複数個の設定ファイルが発見された場合は、全ての設定内容がマージされていきます。
このrootオプションは、このオプションがtrueになっている設定ファイルが見つかるとその時点で探索を終了させてくれます。


# env

envオプションは、チェック対象のJavaScriptやTypeScriptのコードがどの実行環境で使われるコードなのかをESLintに伝えてくれます。
このオプションがあると何が嬉しいかといいますと、ESLintがグローバル変数を認識できるようになります。

例えば、TypeScriptのコードをフロントエンド(ブラウザ)で使うのであれば、envオプションの中にbrowser: trueとすることで、alertwindowといったグローバル変数をちゃんとグローバル変数として認識してくれます。

他にもサンプルファイルにもあるように、es2021: trueを追加するとES2021までに導入されたグローバル変数が認識されます。

他にもnodeなどの実行環境が指定ができます。(指定可能な実行環境の一覧は公式ドキュメントにあります。)

【補足】
このenvオプションの設定はESLintのno-undefルールに関係します。
といいますのも、フロントエンド側のTypeScriptのコードの中でwindowなどを普通に使ってても、問題なく使用できますし動きます。

がしかし、ブラウザで実行されることを知らないESLintからすると、未定義の変数が使われていると認識するため警告が出てしまうでしょう。
そのため、envオプションは正しく設定する必要があります。


# parserOptions

parserOptionsはチェック対象のjavascriptのコードがどの構文を使用しているかをESLintに伝えるためのオプションです。

  • ecmaVersion

    これは、どのバージョンのECMAScriptを使用するか指定するオプションです。
    デフォルトでは、ECMAScript 5が指定されていますが、これは非常に古いバージョンですのでベット指定してあげる必要があります。
    また、envオプションでes2022のようなECMAScriptのバージョンを指定している場合、ecmaVersionにも自動的にes2022のようにバージョンが指定されるため、省略が可能です。

  • sourceType

    JavaScriptには、スクリプトモードモジュールモードの二つのモードがあります。
    sourceTypeオプションは、チェック対象のJavaScriptのコードがどっちのモードで書かれているかをESLintに伝えるためのオプションです。
    デフォルトでは、スクリプトモードが指定されています。
    モジュールモードを指定するとimport / export構文がサポートされます。
    基本的には、モジュールモードを指定します。(sourceType: "module"

# ESLintのルールを設定する

ESLintには「ルール(rule)」という概念があり、ルールはチェックの最小単位です。
ルールには、例えば以下のようなものがあります。

  • no-console: console.logを書いてはならない
  • camelcase: 変数名はキャメルケースにすること
  • semi: 文末セミコロンは省略しない

ESLintでは200を超える数のルールが用意されています。
全てのルールについてはこちらの公式ドキュメントに記載されています。

### 重大度について

ESLintのルールには重大度(severity) という重み付けを設定することができます。
重大度には三つのステータスがあります。

  1. off: ルールを無視し、チェックを行わなくする設定。数値は0
  2. warn: 警告は表示させるが、eslintコマンドの終了コードには影響しません。数値は1
  3. error: 発見した問題をエラーとして報告し、終了コードを1にします。数値は2

### ルールの書き方

ルール(rule)は設定ファイル(.eslintrc.js)のruleフィールドにルール名: 重大度というキー: バリューの形式で記述します。

rules: {
	"no-console": "error",
},

### 個々のルールの細かい設定について

実はESLintの個々のルールの中には、それぞれの細かいルールを設定できるというオプションもあります。
わかりやすいところでいうと、 camelcaseオプションがあります。
これは変数名がキャメルケースかをチェックするルールです。

例えば、変数名にスネークケースを使用したい場合が発生するかもしれません。
というのも、Web APIによってはJSONのキーがスネークケースケースになっていることがあるからです。(例: Railsなどは慣習としてスネークケースを使うことが多い)

そういった場合は、ルール名: [重大度, 設定値]のような配列の形式で設定することで細かいルールを決めることができます。
次の設定例は、プロパティ名に限ってはキャメルケースを強制しない設定です。

rules: {
	"no-console": "error",
	camelcase: ["error", { properties: "never" }],
},

# JavaScriptファイルのlintチェックやってみる

【JavaScriptのサンプルコード】
※ linterに引っ掛かるために今回はわざと下記のようなコードを書きました。

no-consolecamelcaseのオプションが引っ掛かるはずです。

src/helloWorld.js
export const hello_world = "Hello World";
console.log(hello_world);

こちらのコードを実際にlintチェックにかけていこうと思います。
下記のコマンドは、srcディレクトリ配下のファイルをチェックさせるためのコマンドです。

npx eslint src

チェック対象のファイルを修正して、上記のコマンドを実行してを繰り返して、lintのコマンドの実行結果に何も表示されなくなれば OKです。

# コードを自動修正する

ESLintのルールの中には、コードを自動的に修正できるというものがあります。
例えば、semiオプションは文末にセミコロンを付けるか付けないかを定めるルールですが、これは自動修正に対応しています。

ここでは、semiを使ってESLintの自動修正をためしてみます。

.eslintrc.js
rules: {
	"no-console": "error",
	camelcase: ["error", { properties: "never" }],
	semi: ["error", "always"],
},

このルール設定では、"always"を指定しています。
これは、文末セミコロンを必須にする設定です。

この"always"を設定した上で、npx eslint srcコマンドを実行すると、

2 errors and 0 warnings potentially fixable with the `--fix` option.

こんな感じのエラーメッセージが表示されます。
これは、--fixオプションをコマンドに追加することで、自動修正が可能であることを意味するメッセージです。

下記のコマンドを実行したのちに、チェックの対象ファイルを覗いてみてください。
おそらく;がなかった箇所に;が新たについているはずです。

npx eslint src --fix

# Shareable configを導入する

上の方でも説明しましたが、ESLintが用意しているルールは200を越え、非常に多いです。
ルールを一つ一つ確認して導入するのは辛みがあります。

そこで、shareable configを導入してみるのがおすすめです。
shareable configとは、「誰かが設定したルールのプリセット」のことです。
これを導入すると、自分でルールを設定する手間が省けるため、非常に便利です。

有名なsharable configのひとつにESLintの公式が公開しているeslint:recommendedというものがあります。
これは、公式ドキュメントのRulesの一覧でチェックマークがついているルールが一括して有効化されます。
これは公式が提供してるため有名ですが、有効になっているルールが少ないため、実務では物足りなさがあるかもしれません。

### 第3者公開のshareable config

第三者が公開しているshareable configもあり、次にあげるものは結構有名な奴です。

名前 作成元 準拠するコーディング規約
eslint-config-airbnb Airbnb Airbnb JavaScript Style Guide、Airbnb React/JSX Style Guide
eslint-config-airbnb-base Airbnb Airbnb JavaScript Style Guide
eslint-config-standard Standard JS JavaScript Standard Style
eslint-config-google Google Google JavaScript Style Guide

上のshareable configはコーディング規約に基づいて作成されているため、文書としてのコーディング規約とESLintの設定をセットでプロジェクトに導入できる利点があります。

eslint-config-airbnb-baseを使ってみる

まず、yarnでeslint-config-airbnb-baseをインストールします。
その際に、合わせてeslint-plugin-importも導入します。

【インストールコマンド】

yarn add -D \
  eslint-config-airbnb-base@^15 \
  eslint-plugin-import@^2

設定ファイル.eslintrc.jsのrulesを消します。その上で、extends: ["airbnb-base"]を追加してください。

.eslint.js
module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true,
  },
  parserOptions: {
    ecmaVersion: "latest",
    sourceType: "module",
  },
  // ココ追加
  extends: ["airbnb-base"],
};

これで、shareable configの導入は完了です。

設定ファイルで特定のルールを上書きする

ここでは名前付きエクスポートを使いたいので、このルールをオフにすることで警告されないようにしてみましょう。
ルールを上書きするには、.eslintrc.jsのrulesに"import/prefer-default-export": "off"を追加します。

.eslintec.js
extends: ["airbnb-base"],
// rulesフィールドでルールを上書きする
rules: {
	"import/prefer-default-export": "off",
},

さらに、文字列リテラルはダブルクォートのほうを使いたいので、rulesにquotes: ["error", "double"]を追加します。

.eslint.js
rules: {
	"import/prefer-default-export": "off",
	// クォーテーションはダブルクォーテーションに指定
	quotes: ["error", "double"],
},

# ルールを部分的に無効化する

.eslintrc.jsに記述したコーディングの規約は良くも悪くも、プロジェクト全体に影響を及ぼします。

しかし、コードを書いているとどうしてもこのコーディング規約を破らないといけないタイミングがあります。

そうした場合、コードの一部分のみについてルールを破ることができます。

// eslint-disable-next-line camelcase
export const hello_world = "Hello World";

上記のサンプルコードのように、eslintのdisableコメントを該当箇所の直前に追加することで、コーディング規約のlintチェックを無効にすることができます。

# TypeScriptをlintする

いよいよ本題のTypeScriptのlintをここから始めていきます。
実際のサバイバルTypeScriptでは、別のプロジェクトを新たに作成していますが、こちらでは先程のプロジェクトを引き続き使っていこうと思います。

# TypeScriptを導入する

TypeScript ESLintを使うにはまず、TypeScript環境を構築しておく必要があります。
まず、typescriptを導入しておいてください。
合わせてNode.jsの型定義@types/nodeもインストールしておきます。
この型情報は、.eslintrc.jsなどのNode.js環境で実行されるファイルをESLintでチェックするときに利用されます。

【上記のインストールコマンド】

yarn add -D typescript@^4.6 @types/node@^16

helloWorld.jsファイルも、helloWorld.tsファイルに拡張子を変更する必要があります。

上記のインストールが完了したら、次にTSコンパイラの諸々の設定を記述するためのtsconfig.jsonファイルを作成・編集していきます。

tsconfig.json
{
  "compilerOptions": {
    "outDir": "dist"
  },
  "include": ["src"]
}

【ちょっと解説】
上記の設定ファイルのoutDirオプションは、TSC(TypeScriptのコンパイラ)がコンパイルを実行した出力結果をどのディレクトリの配下に置くかを指定するもの。

includeオプションは、コンパイルの対象となるファイルがどのディレクトリにあるかをTSCに伝えるためのもの。

下記のコンパイルコマンドを実行すると、/distディレクトリの配下にhelloWorld.jsというコンパイルの出力結果のファイルが生成されているはず。

コンパイルコマンド

npx tsc

※distディレクトリ配下のコンパイル出力ファイルはgithubにpushする必要がないので、.gitignoreファイルに追加しておきます。

# TypeScript ESLintを導入する

TypeScriptをESLintにチェックさせるには、ESLint本体とTypeScript ESLintの両方をインストールする必要があります。

こちらでは、先程のJavaScriptをESLintでチェックさせる部分でESLint本体は入っているので、TypeScript ESLintのみインストールしておきます。

インストールコマンド

yarn add -D \
  @typescript-eslint/parser@^5 \
  @typescript-eslint/eslint-plugin@^5

TypeScript ESLintは二種類のパッケージから成り立ちます。

  1. @typescript-eslint/parser: ESLintにTypeScriptの構文を理解させるためのパッケージ。
  2. @typescript-eslint/eslint-plugin: TypeScript向けのルールをESLintに追加するためのパッケージ。

# TypeScript ESLintにはどんなルールがあるのか?

ESLintの200以上のルールに加えて、TypeScript ESLintを導入すると、100以上のルールが追加されます。
追加されるルールの一覧は、TypeScript ESLintのドキュメントで確認できます。

# TypeScript向けのshareable configを導入する

コーディング規約Airbnb JavaScript Style Guideに準拠したshareable configをインストールします。

ただし今回の場合は、すでにJavaScriptの方はインストールしているのでTypeScriptの方(eslint-config-airbnb-typescript)を今回はインストールする。

yarn add -D eslint-config-airbnb-typescript@^17

eslint-config-airbnb-baseはJavaScript向けのshareable configです。(上記で登場済み)
これを上書きして、TypeScript ESLintのルールを追加したり、TypeScriptコンパイラがチェックするためESLintでチェックする必要がないルールを除外する設定を加えるのがeslint-config-airbnb-typescriptです。

※eslint-plugin-importは依存関係上、導入が必要なパッケージです。

# TypeScript ESLintの設定ファイルを作る

TypeScript ESLintを動かすためには、次の2つの設定ファイルを作る必要があります。
(※これってeslintrc.jsファイル一つでやってるとことかない?)

  1. tsconfig.eslint.json
  2. eslintrc.js

これらファイルをプロジェクトルートに作成します。

### tsconfig.eslint.jsファイルについて

TypeScript ESLintでは、lintのチェック時に型情報を利用するためにTSコンパイラを使用します。
その際のコンパイラの設定をtsconfig.eslint.jsonファイルに記載します。

コンパイラの具体的な設定については、tsconfig.jsonファイルをextendsで継承しつつ、上書きが必要な部分のみ記載します。

tsconfig.eslint.json
{
	// 継承する設定内容が記述されているconfigファイルを指定する
	"extends": "./tsconfig.json"
}

今回は、TypeScriptで記述されたスクリプトファイルだけではなく、ESLintの設定ファイル(.eslintrc.js)ファイル自体もチェックの対象に含めたいと思いますので、allowJsオプションの追加とincludeオプションの上書きをします。

tsconfig.eslint.json
{
	"extends": "./tsconfig.json",
	"compilerOptions": {
		"allowJs": true
	},
	"include": ["src", ".*.js"]
}

allowjsオプションをtureにすると、*.js*.jsxなどの拡張子のファイルも.js*.jsxもコンパイル対象に含まれるようになる。
※ただしTSではないので、型のチェックが行われるとかではなく、トランスパイルの対象となる。

このようにTypeScript ESLintでチェックする対象は、includeオプションに追加していく必要があります。

tsconfig.eslint.jsonが正しく設定されているか、次のコマンドを実行して出力を確認して見ます。

npx tsc --showConfig --project tsconfig.eslint.json

次に、ESLintの設定ファイルを編集していきます。

eslintrc.js
module.exports = {
	root: true,
	parser: "@typescript-eslint/parser",
	plugins: ["@typescript-eslint"],
	env: {
		browser: true,
		es2021: true,
	},
	parserOptions: {
		ecmaVersion: "latest",
		sourceType: "module",
		project: "./tsconfig.eslint.json",
		tsconfigRootDir: __dirname,
	},
	ignorePatterns: ["dist"],
	extends: [
		"airbnb-base",
		"airbnb-typescript/base",
		"plugin:@typescript-eslint/recommended-requiring-type-checking",
	],
	rules: {
		"import/prefer-default-export": "off",
		"@typescript-eslint/quotes": ["error", "double"],
	},
};

上記の設定ファイルのオプションの中で、rootenvparserOptionsecmaVersionsourceTypeについては説明済みですので省略しようと思います。

# parser

eslintrc.js
module.exports = {
	// ...
	parser: "@typescript-eslint/parser",
	// ...
};

このparserオプションで設定したパーサーを使用して、eslintはJavaScriptコードやTypeScriptコードの構文の解析を行います。

上記のサンプルでは、TypeScriptのパーサーを指定しています。

このパーサーの指定を行わないと、eslintがTypeScriptの構文の解釈ができず、エラーが発生します。

※【補足】
TypeScriptはJavaScriptの構文を拡張した言語です。
なので、このパーサーさえ入れておけば、TypeScriptに限らずJavaScriptのこのパーサーひとつで対応できます。
要するに、このパーサーひとつで、TypeScriptとJavaScriptのファイルどちらもリントできるようになります。


# plugins

eslintrc.js
module.exports = {
	// ...
	plugins: ["@typescript-eslint"],
	// ...
};

eslintでは、第三者が公開したルールを使うこともできます。
この第三者が作成したルールは、「プラグイン」という形で公開されています。

eslintでは、pluginsというオプションにプラグインを追加していくことで、新たなルールを追加することができます。

上の例では、TypeScript ESLint独自のルールを追加するために、@typescript-eslintを設定しています。


# parseOptions

eslintrc.js
module.exports = {
	// ...
	parserOptions: {
		// ...
		project: "./tsconfig.eslint.json",
		tsconfigRootDir: __dirname,
	},
	// ...
};

上記の設定のサンプルで設定されているprojecttsconfigRootDirはTypeScript独自のオプションです。

tsconfigRootDirはプロジェクトルートの絶対パスを指定します。
projectは、ESLint実行時に使うコンパイラ設定ファイル(今回ならtsconfig.eslint.jsonファイル)をtsconfigRootDirからの相対パスで指定します。
これらの設定は、TypeScript ESLintが型情報を参照するために必要な設定です。


# ignorePatterns

eslintrc.js
module.exports = {
	// ...
	ignorePatterns: ["dist"],
	// ...
};

ignorePatternsオプションは、eslintのチェックの対象外にするファイルやディレクトリを指定できるオプションです。

基本的には、コンパイルの実行によって生成されるファイルやディレクトリはチェックの対象外にします。
※今回は、コンパイルの実行結果が/distディレクトリ配下に生成されるので、上記のような指定をしています。


# extends

eslintrc.js
module.exports = {
	// ...
	extends: [
		"airbnb-base", // ①
		"airbnb-typescript/base", // ②
		"plugin:@typescript-eslint/recommended-requiring-type-checking", // ③
	],
	// ...
};

extendsはshareable configを使用するためのオプションです。
①は、JavaScript向けのルールです。
これを拡張してTypeScript ESLintのルールにも範囲を広げたのが②です。

※①と②は上の順番でないと正しく設定されないので注意してください。

③はTypeScript ESLintが提供する推奨ルールセットで、型情報を要するルールを含みます。
このルールセットでどのルールが有効になるかは、公式ドキュメントをご覧ください。


# rules

eslintrc.js
module.exports = {
	// ...
	rules: {
		"import/prefer-default-export": "off",
		"@typescript-eslint/quotes": ["error", "double"],
	},
	// ...
};

最後に、rulesというオプションについてです。
ここのrulesは、shareable configで有効化されたルールを上書きするのに用いています。TypeScript ESLintで追加されたルールは、@typescript-eslint/が接頭辞になります。

基本的にはrulesの部分に様々なTypeScriptのチェックに関するルールを追加する。


# 最後に

ここまでで、TypeScript ESLintを導入してチェックするための設定が一通り完了です。
最後にnpx eslint .コマンドでチェックを実行し確認することができます。

Discussion

ログインするとコメントできます