かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Next.js ESLint で TypeScript のエラーを表示するようにしたい

TypeScript Next.js ESLint の設定でハマったついでにクリーンな環境で Next.js の ESLint で TypeScript のエラー表示させる方法までを試したのでメモ代わりに残しておきます

環境

  • Next.js 13.2.1
  • react 18.2.0
  • typescript: 4.9.5
  • eslint 8.35.0

Next.js のプロジェクト作成

$ npx create-next-app <PROJECT NAME> --typescript
✔ Would you like to use ESLint with this project? … Yes
✔ Would you like to use `src/` directory with this project? … Yes
✔ Would you like to use experimental `app/` directory with this project? … Yes
✔ What import alias would you like configured? … @/*

デフォルトの ESLint

.eslintrc.json

{ "extends": ["next/core-web-vitals"] }

TypeScript 的にやばいコンポーネントを作成する

React Component/src/MyComponent.tsx

import { FC } from "react";
type MyComponentProps = {
  name: string;
};
export const MyComponent: FC = ({ user }) => {
  const foo = user();
  const bar: any = user;
  return <div>{user.name} {user.age} {foo} {bar}</div>
};
  • MyComponentProps -> 使われてない変数
  • const MyComponent: FC = ({ user } -> user が any
  • const foo = user(); -> any 型に対してメソッドの呼び出し
  • const bar: any = user; -> any 型の宣言, bar は使用されてない変数
  • {user.name} {user.age} -> any 型のプロパティアクセス

Next Page /src/app/sample/page.tsx

import { NextPage } from "next";
const SamplePage: NextPage = ({ members }) => {
  return <div>{members.map((user) => (<div key={user.id} >{user.name}</div>))}</div>
};
export default SamplePage;
  • const SamplePage: NextPage = ({ members }) -> members は any 型
  • members.map((user) => (<div key={user.id} >{user.name}</div>))
    • -> any型の members に対して .map の実行
    • -> user は any 型
    • -> any 型の user のプロパティにアクセス

📝 デフォルトの ESLint では TypeScript のエラーが出ない

$ npm run lint
✔ No ESLint warnings or errors

.tsconfig.json"strict": true だが ESLint で any のエラーなどを拾うことができない状態…

Next.js の ESLint で TypeScript のエラーを表示するようにする

$ npm i -D @typescript-eslint/eslint-plugin

未使用の変数と any の型指定に warning を表示させる設定を .eslintrc.json に追加した

{
- "extends": ["next/core-web-vitals"],
+ "extends": [
+   "plugin:@typescript-eslint/recommended",
+   "next/core-web-vitals"
+ ],
+ "rules": {
+   "@typescript-eslint/no-unused-vars": "warn",
+   "@typescript-eslint/no-explicit-any": "warn"
+ }
}

👇 ESLint の実行

$ npm run lint
./src/MyComponent.tsx
3:6  Warning: 'MyComponentProps' is defined but never used.  @typescript-eslint/no-unused-vars
9:9  Warning: 'bar' is assigned a value but never used.  @typescript-eslint/no-unused-vars
9:14  Warning: Unexpected any. Specify a different type.  @typescript-eslint/no-explicit-any

意図したとおりに Warning が表示されている

any に対するルールの追加

  • no-unsafe-call … any型に対する関数呼び出し(anyVal()など)を禁止します。
  • no-unsafe-member-access … any型に対するメンバ呼び出し(anyVal.hogeやanyVal['hoge']など)を禁止します。
  • no-unsafe-return … anyかany[]が引数の返り値になることを禁止します。

cf. typescript-eslintの最新オプションno-unsafe-*を使って、TypeScriptの型リファクタリングを簡単に行った話 - ITANDI Engineer Blog

.eslintrc.json を編集する

{
  "extends": [
    "plugin:@typescript-eslint/recommended",
    "next/core-web-vitals"
  ],
+ "parserOptions": {
+   "project": "./tsconfig.json"
+ },
  "rules": {
    "@typescript-eslint/no-unused-vars": "warn",
    "@typescript-eslint/no-explicit-any": "warn",
+   "@typescript-eslint/no-unsafe-call": "error",
+   "@typescript-eslint/no-unsafe-member-access": "error",
+   "@typescript-eslint/no-unsafe-return": "error"
  }
}

※ 違いが見やすいように "error" の指定にした

no-unsafe-call, no-unsafe-member-access, no-unsafe-return のルールを使うには parserOptions.project にプロジェクトの tsconfig を指定する必要がある
指定してないと lint 実行時に次のようなエラーになる => Error while loading rule '@typescript-eslint/no-unsafe-call': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.

👇 ESLint の実行

$ npm run lint
./src/MyComponent.tsx
3:6  Warning: 'MyComponentProps' is defined but never used.  @typescript-eslint/no-unused-vars
8:15  Error: Unsafe call of an `any` typed value.  @typescript-eslint/no-unsafe-call
9:9  Warning: 'bar' is assigned a value but never used.  @typescript-eslint/no-unused-vars
9:14  Warning: Unexpected any. Specify a different type.  @typescript-eslint/no-explicit-any
10:29  Error: Unsafe member access .name on an `any` value.  @typescript-eslint/no-unsafe-member-access
10:41  Error: Unsafe member access .age on an `any` value.  @typescript-eslint/no-unsafe-member-access

./src/app/sample/page.tsx
4:16  Error: Unsafe member access .map on an `any` value.  @typescript-eslint/no-unsafe-member-access
4:16  Error: Unsafe call of an `any` typed value.  @typescript-eslint/no-unsafe-call
4:49  Error: Unsafe member access .id on an `any` value.  @typescript-eslint/no-unsafe-member-access
4:60  Error: Unsafe member access .name on an `any` value.  @typescript-eslint/no-unsafe-member-access

先程は表示されなかった any 型のオブジェクトに対するアクセスにもルール通りエラーが表示されるようになった!

まとめ

  • Next.js はデフォルトでは TypeScript のエラーが npm run lint では表示されない
  • ミニマムな TypeScript のエラーを表示させるには @typescript-eslint/eslint-plugin をインストールして .eslintrc.json にルールを追加する
  • 使用するルールに依っては .eslintrc.jsonparserOptions.project プロパティを作成しプロジェクトの tsconfig.json のパスを指定する

自分で試した結果なので、勘違いなどあるかもしれません :pray:
おわり ₍ ᐢ. ̫ .ᐢ ₎


[参考]