eslintと仲良くなりたい(3)

前回のおさらい

前回、prettierとのコンフリクトを解消するための設定を追加しコードスタイルについてはprettierでフォーマットするようにできた。

import js from "@eslint/js";
import prettier from "eslint-config-prettier";
import globals from "globals";

export default [
  {
    languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.node,
      },
    },
  },
  js.configs.recommended,
  {
    rules: {
      "no-unused-vars": "off",
      "no-var": "error",
    },
  },
  prettier,
];

サンプルコードはこれ。

const me = "naoty";
console.log("hello");

今回はTypeScriptのための設定を追加していく。

事前準備

TypeScriptの環境を簡単にセットアップするためviteを使い、今まで使っていたサンプルコードも拡張子を*.tsに変更する。

この時点でeslintを実行するとwarningになる。

% npx eslint src/main.ts
/home/naoty/repos/localhost/hello-eslint/src/main.ts
  0:0  warning  File ignored because no matching configuration was supplied

✖ 1 problem (0 errors, 1 warning)

TypeScriptのための設定がないため、無視されてしまうようだ。

typescript-eslint

typescript-eslintを使うとTypeScriptのコードに対してeslintを実行できるようになる。

 import eslint from "@eslint/js";
 import prettier from "eslint-config-prettier";
 import globals from "globals";
+import tseslint from "typescript-eslint";
 
-export default [
+export default tseslint.config(
   {
     languageOptions: {
       globals: {
         ...globals.browser,
         ...globals.node,
       },
     },
   },
   eslint.configs.recommended,
+  tseslint.configs.recommended,
   {
     rules: {
       "no-unused-vars": "off",
       "no-var": "error",
     },
   },
   prettier,
-];
+); 

config関数はflat configに型情報を付与するためのユーティリティ関数であり、実行結果は変わらないとのこと。typescript-eslintが提供するルールセットにもeslintと同じく推奨設定があるため、これを追加した。

実行してみる。

% npx eslint
/home/naoty/repos/localhost/hello-eslint/src/main.ts
  1:7  error  'me' is assigned a value but never used  @typescript-eslint/no-unused-vars

✖ 1 problem (1 error, 0 warnings)

今度は無視されずにエラーが出るようになった。no-unused-varsは無効にしていたが、typescript-eslint版のルールが別にあるようなので、同じく無効にしてみる。

 export default tseslint.config(
   {
     languageOptions: {
       globals: {
         ...globals.browser,
         ...globals.node,
       },
     },
   },
   js.configs.recommended,
   tseslint.configs.recommended,
   {
     rules: {
-      "no-unused-vars": "off",
+      "@typescript-eslint/no-unused-vars": "off",
       "no-var": "error",
     },
   },
   prettier
 );
% npx eslint

エラーがなくなった。

他の推奨設定

typescript-eslintにはrecommended以外にもいくつか推奨設定のルールセットがある。ここに整理されている。

recommended-type-checked

recommendedこの設定を追加したもの。

追加された設定をいくつか見てみる。

  • await-thenable: Promiseのようなthenメソッドを持つオブジェクト以外に対してawaitを呼び出そうとしたらエラーにする。
  • no-unsafe-returnなどのno-unsafe-*: any型を許可しないルールたち
  • only-throw-error: Errorオブジェクトのみthrowを許可する。RemixだとResponsethrowすることがあるので、オプションで一部許容したいかも。

こう見ると、追加してよさそうなルールなので、使ってみたい。

 export default tseslint.config(
   {
     languageOptions: {
       globals: {
         ...globals.browser,
         ...globals.node,
       },
     },
   },
   js.configs.recommended,
-  tseslint.configs.recommended,
+  tseslint.configs.recommendedTypeChecked,
   {
     rules: {
       "@typescript-eslint/no-unused-vars": "off",
       "no-var": "error",
     },
   },
   prettier
 );

実行してみる。

Oops! Something went wrong! :(

ESLint: 9.15.0

Error: Error while loading rule '@typescript-eslint/await-thenable': You have used a rule which requires type information, but don't have parserOptions set to generate type information for this file. See https://typescript-eslint.io/getting-started/typed-linting for enabling linting with type information.
Parser: typescript-eslint/parser

エラーになってしまった。メッセージを読むと、型情報を生成するために必要なパーサーのオプションが設定されていないとのことだった。リンク先を読んで設定を追加する。

 export default tseslint.config(
   {
     languageOptions: {
       globals: {
         ...globals.browser,
         ...globals.node,
       },
+      parserOptions: {
+        projectService: true,
+        tsconfigRootDir: import.meta.dirname,
+      },
     },
   },
   js.configs.recommended,
   tseslint.configs.recommendedTypeChecked,
   {
     rules: {
       "@typescript-eslint/no-unused-vars": "off",
       "no-var": "error",
     },
   },
   prettier
 );

またtsconfig.jsonも必要になるので追加する。@tsconfig/node22を使って楽をする。

+{
+  "extends": "@tsconfig/node22/tsconfig.json",
+  "compilerOptions": {
+    "lib": ["es2023", "dom"],
+  }
+}

consoleに型情報を付与するためlibdomを追加してある。

実行してみる。

% npx eslint
/home/naoty/repos/localhost/hello-eslint/eslint.config.js
  0:0  error  Parsing error: /home/naoty/repos/localhost/hello-eslint/eslint.config.js was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject

✖ 1 problems (1 errors, 0 warnings)

eslint.config.jsのような*.jsのファイルについてパースに失敗している。*.tsのファイルにのみrecommended-type-checkedのルールを適用したいので、filesを使って適用するファイルを絞る。

 export default tseslint.config(
   {
     languageOptions: {
       globals: {
         ...globals.browser,
         ...globals.node,
       },
-      parserOptions: {
-        projectService: true,
-        tsconfigRootDir: import.meta.dirname,
-      },
     },
   },
   js.configs.recommended,
-  tseslint.configs.recommendedTypeChecked,
   {
     rules: {
-      "@typescript-eslint/no-unused-vars": "off",
       "no-var": "error",
     },
   },
+  {
+    files: ["**/*.ts"],
+    languageOptions: {
+      parserOptions: {
+        projectService: true,
+        tsconfigRootDir: import.meta.dirname,
+      },
+    },
+    extends: [tseslint.configs.recommendedTypeChecked],
+    rules: {
+      "@typescript-eslint/no-unused-vars": "off",
+    },
+  },
   prettier
 );

filesでTypeScriptのファイルにのみrecommended-type-checkedやparserOptionsを適用している。extendsはtypescript-eslintのconfig()関数によって追加されているプロパティで、これにより定義済みの設定をrulesで上書きする設定が書きやすくなっている。

実行すると失敗しなくなった。

% npx eslint

strict, strict-type-checked

recommendedに加えて厳しいルールを追加したルールセットで、TypeScriptに自信のあるチームには推奨とのことだった。

自信ないのでいったん見送る。

To Be Continued...

最後にeslintの設定もfilesを使った書き方に体裁をそろえる。

 export default tseslint.config(
   {
     languageOptions: {
       globals: {
         ...globals.browser,
         ...globals.node,
       },
     },
   },
-  js.configs.recommended,
-  {
-    rules: {
-      "no-var": "error",
-    },
-  },
+  {
+   files: ["**/*.{js,ts}"],
+   extends: [eslint.configs.recommended],
+   rules: {
+     "no-var": "error",
+   },
+  },
   {
     files: ["**/*.ts"],
     languageOptions: {
       parserOptions: {
         projectService: true,
         tsconfigRootDir: import.meta.dirname,
       },
     },
     extends: [tseslint.configs.recommendedTypeChecked],
     rules: {
       "@typescript-eslint/no-unused-vars": "off",
     },
   },
   prettier
 );

今回はTypeScript向けにeslintを設定しつつ、その中でfilesによって特定のファイルのみに設定を適用する書き方についても見ることができた。

次回はReactの開発で便利なeslintの設定を見ていきたいです。

その(4)