typescript-eslintのFlat Configについて、自分に今必要そうな部分だけをひととおり確認したので忘れる前にメモを残しておく。
前提
- 素のJavaScriptプロジェクトをやることは自分はあまりなさそうなのでTypeScript前提
- ES Modules前提でいいやと思っているので設定ファイルの拡張子はシンプルに
.js
にする - フォーマッターにはESLintのStylisticじゃなくてESLint外のフォーマッター(PrettierやBiome)を使う前提
基本の設定
https://typescript-eslint.io/getting-started/ の最初に書いてある設定。
// @ts-check import eslint from '@eslint/js'; import tseslint from 'typescript-eslint'; export default tseslint.config( eslint.configs.recommended, tseslint.configs.recommended, );
この設定の意味
**/*.js, **/*.cjs, and **/*.mjs
と'**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'
に対してeslint.configs.recommended
とtseslint.configs.recommended
が適用される
シンプルだね。
参照:推奨ルールの実装
eslint.configs.recommended
tseslint.configs.recommended
tseslint.config() って何?
ESLintのドキュメントでは配列で定義しているのに、typescript-eslintのドキュメントでは tseslint.config()
を使っている。どうしてだろう?って思って調べた。
↓ESLintでは配列で定義している。https://eslint.org/docs/latest/use/configure/configuration-files より。
export default [ { rules: { semi: "error", "prefer-const": "error" } } ];
↓こちらの記事を参考にして追いかけた。ありがたい。
次の2点だった。
- 設定を型安全に書けるようになる
extends
を使えるように拡張されている
tseslint.config()
は設定の配列を返す単純なラッパーになっている。なので tseslint.config()
を使わなくても設定は書ける。でも、これを使うと簡単に型安全な設定を書ける。
だから tseslint.config()
は使おうと思う。
一方で、これは個人的な好みの問題だけど extends
はできれば使いたくないな。typescript-eslint独自の拡張だから。
参照:tseslint.config() の実装
...
がついてる例とついてない例を見かけるよね?
さっきはこういう設定↓を書いたけど
export default tseslint.config( eslint.configs.recommended, tseslint.configs.recommended, );
いろいろ調べてると、こんな設定の例↓を見かけるときもある。配列が展開されている。
export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommended, );
これはどちらでもいい。...
をつけなくても tseslint.config()
が配列を展開してくれる。僕は前者の ...
をつけない方で書こうかな。
適用対象のファイルがどう決まっているのか?
さて、さっき↓こういう設定に対して
export default tseslint.config( eslint.configs.recommended, tseslint.configs.recommended, );
「**/*.js, **/*.cjs, and **/*.mjs
と '**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'
が適用対象になる」と言ったがこれはどう決まっているのか?
Flat Configでのfilesの扱い
Flat Configは設定の配列になっている。そして、この中に定義している files
によって対象となるファイルが決まる。
- デフォルトの対象ファイルは
**/*.js, **/*.cjs, and **/*.mjs
files
が定義されていない設定の場合- デフォルトの対象ファイルに適用される
- それに加えて他にfilesを指定している設定があれば、そのファイルにも適用される
files
が定義されている設定の場合- 指定されたファイルのみに適用される
- ファイルが複数の設定にマッチする場合
- 設定がマージされて適用される。下に定義してある設定によって上書きされる形でマージされる
参照
By default, ESLint lints files that match the patterns
**/*.js, **/*.cjs, and **/*.mjs
https://eslint.org/docs/latest/use/configure/configuration-files#specifying-files-and-ignores
展開して見てみよう
さきほどの設定のrecommendedを展開するとこういう感じになる。
export default [ // eslint.configs.recommendedを展開したもの // (1) { rules: { // ESLintの推奨ルール群 } }, // tseslint.configs.recommendedを展開したもの // (2) { name: 'typescript-eslint/base', // '@typescript-eslint'の有効化とパーサーの設定 languageOptions: { parser, sourceType: 'module', }, plugins: { '@typescript-eslint': plugin, }, }, // (3) { name: 'typescript-eslint/eslint-recommended', files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'], rules: { // ESLint推奨ルール群の中で // TypeScriptがチェックしてくれるものはオフにしている // またTypeScriptに合わせてオンにしたほうがいいものはオンにしている } }, // (4) { name: 'typescript-eslint/recommended', rules: { // typescript-eslintの推奨ルール群 } } ];
(1)(2)(4)には files
が定義されていない。(3)にだけ files
が定義されている。つまり次のようになる。
- (1)(2)(4)
- ESLintデフォルトの
**/*.js, **/*.cjs, and **/*.mjs
に適用される - (3)のファイルも適用対象となるので
'**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'
にも適用される
- ESLintデフォルトの
- (3)
files
で指定された'**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'
だけに適用される
重ね合わせを考えると
- JSのファイルの場合→(1)(2)(4)の重ね合わせになる
- TSのファイルの場合→(1)(2)(3)(4)の重ね合わせになる
(3)ではESLintの推奨ルール群をTypeScript用に上書きしているので、その上書きはTSのファイルに対してのみ有効になっている。
ちなみに(2)でTypeScript用のプラグインとパーサーが指定してあるのでTSファイルを解析できている。
参照:eslint-recommended-raw.ts
tseslint.configs.recommended
が eslint.configs.recommended
のルールの一部をTypeScriptに合わせて上書きしている部分は eslint-recommended-raw.ts
に定義されている。
TypeScriptの型情報が必要なルール
ここまで tseslint.configs.recommended
を見てきたが、TypeScriptの型情報が必要なルールは別のセットになっている。なので、それを適用したい場合は recommendedTypeChecked
を使う。
export default tseslint.config( eslint.configs.recommended, tseslint.configs.recommendedTypeChecked, );
recommendedTypeChecked
には tseslint.configs.recommended
の内容も含まれているので両方を書くのではなく recommendedTypeChecked
だけを書けばいい。
ただ、これだけでは正しく動作しない。パーサーに型情報を生成するように設定しないといけない。次のように書く。
export default tseslint.config( eslint.configs.recommended, tseslint.configs.recommendedTypeChecked, { languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }, );
これで型情報が必要なルールも適用される。ふつうは型情報を必要とするルールを適用したいだろうと思うので、recommended
よりも recommendedTypeChecked
をメインで使いそう。
別セットに分かれている理由は、デフォルトの推奨ルールを速くするためらしい。ふむふむ。
導入を容易にするため、デフォルトのルールを速くするため、とのこと。
参照:recommendedTypeCheckedの実装
tseslint.configs.recommendedTypeChecked
除外したいファイル
Flat Configの設定で ignores
を指定すると設定の対象から除外できる。デフォルトでは "**/node_modules/", ".git/"
が除外対象になっている。
This pattern is added after the default patterns, which are ["**/node_modules/", ".git/"]. https://eslint.org/docs/latest/use/configure/ignore#ignoring-files
ignores
が単独で使用されると特別な意味を持つことに注意
ignores
が単独で使用されると、すべての設定に適用される。ignores
が他の項目と一緒に使用されると、その設定のみに除外設定が適用される。
export default [ { ignores: [".config/*"] } ];
この場合 "**/node_modules/", ".git/"
に加えて .config
ディレクトリがすべての設定から除外される。
例えばビルド出力用のフォルダを dist
にしている場合は、↓こんな風に定義しておくと良さそう。
export default tseslint.config( { ignores: ["dist/"], }, eslint.configs.recommended, tseslint.configs.recommendedTypeChecked, { languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }, );
フォーマッタについて
最後にフォーマッタについて書いておく。ファイルのフォーマットにPrettierを使うにしてもBiomeを使うにしても、ESLintのフォーマットまわりのルールをオフにしておきたいので eslint-config-prettier
を設定の最後に追加しておく。
// @ts-check import eslint from '@eslint/js'; import prettierConfig from 'eslint-config-prettier'; import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: ["dist/"], }, eslint.configs.recommended, tseslint.configs.recommendedTypeChecked, { languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }, prettierConfig, // 途中の設定がONにしていてもOFFになるように、最後に追加しておく );
こんなところかな。これで最近調べたことをいったん忘れても大丈夫そう。よかった。
参照
ESLintのドキュメント
- Configuration Files - ESLint - Pluggable JavaScript Linter
- Ignore Files - ESLint - Pluggable JavaScript Linter
typescript-eslintのドキュメント
- Getting Started | typescript-eslint
- Linting with Type Information | typescript-eslint
- What About Formatting? | typescript-eslint
参考にした記事