[TypeScript][ESLint]@typescript-eslint/strict-boolean-expressionsでTruthy/Falsyの判定を禁止する

2022.02.14

吉川@広島です。

JS/TSのコードに触れていると、以下のようなTruthy/Falsyを利用したnullチェックを見ることがあると思います。

const foo: Foo | undefined = getFoo()
if (foo) {
  // fooがundefinedでない場合の処理
}

個人的に、Truthy/Falsyを使った判定は後からコードを見返した際に「nullチェックをしているのか」「0をチェックしているのか」「空文字 "" をチェックしているのか」など一瞬読解に迷う場合があるため、より厳密な判定をする方が好みです。

例えばnullチェックであれば != null と書くようにした方が意図が明確に伝わりやすいと思っています。

if (foo != null) {
  // fooがundefinedでない場合の処理
}

!== null ではなく != null と書くのはnullとundefinedを一度にチェックするイディオムです。

このようにTruthy/Falsyを避けるコードスタイルをプロジェクトで統一したい場合、「各自書き方を気をつける」だとなかなか徹底か難しいと思います。この点、最近ESLintの@typescript-eslint/strict-boolean-expressionsを発見しまして、非常に使えそうだったため紹介します。

環境

  • node 16.13.2
  • typescript 4.5.5
  • eslint 8.9.0
  • eslint-config-prettier 8.3.0
  • eslint-config-airbnb-base 15.0.0
  • eslint-plugin-import 2.25.4
  • @tsconfig/recommended 1.0.1

@typescript-eslint/strict-boolean-expressionsルールについて

typescript-eslint/strict-boolean-expressions.md at main · typescript-eslint/typescript-eslint

Restricts the types allowed in boolean expressions. Forbids usage of non-boolean types in expressions where a boolean is expected. boolean and never types are always allowed. Additional types which are considered safe in a boolean context can be configured via options. The following nodes are considered boolean expressions and their type is checked:

「booleanが期待される表現において、boolean以外の型の使用を禁止します」(DeepL翻訳)ということなので、すなわちTruthy/Falsyの利用の禁止であると理解しています。

試してみる

tsconfig設定

tsconfig.json を用意します。設定は@tsconfig/recommendedを使って楽をします。

npm i -D @tsconfig/recommended
{
  "extends": "@tsconfig/recommended/tsconfig.json"
}

サンプルコード

下記のサンプルコードを用意します。

// index.ts

interface Foo {
  name: string
}

const getFoo = (): Foo | undefined => ({ name: 'foo' })

const foo = getFoo()
if (foo) {
  // Do something...
}

if (foo) { のnullチェックにエラーが出ることが期待値となります。

eslint設定 && 実行

本当は npx eslint --init でscaffoldしたんですが、バージョンが変わると再現が難しくなるかもしれないため下記の手順で紹介します。

まずは依存関係をインストールします。

npm i -D eslint eslint-config-airbnb-base eslint-plugin-import eslint-config-prettier

.eslintrc.yml を作成します。

# .eslintrc.yml

env:
  browser: true
  es2021: true
extends:
  - airbnb-base
  - prettier
parser: '@typescript-eslint/parser'
parserOptions:
  ecmaVersion: latest
  sourceType: module
plugins:
  - '@typescript-eslint'
rules: {}

@typescript-eslint/strict-boolean-expressions のルールをONにします。

env:
  browser: true
  es2021: true
extends:
  - airbnb-base
  - prettier
parser: '@typescript-eslint/parser'
parserOptions:
  ecmaVersion: latest
  sourceType: module
plugins:
  - '@typescript-eslint'
-rules: {}
+rules:
+ '@typescript-eslint/strict-boolean-expressions':
+   - error
+   - allowString: false
+     allowNumber: false
+     allowNullableObject: false
+     allowNullableBoolean: false
+     allowNullableString: false
+     allowNullableNumber: false
+     allowAny: false

allow* をすべて false にする最も厳しい設定にしています。

上記設定を行った後、

npx eslint index.ts

でLintを実行します。すると下記のエラーが出てしまいました。

Oops! Something went wrong! :(

ESLint: 8.9.0

Error: Error while loading rule '@typescript-eslint/strict-boolean-expressions': 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.

parserOptions の設定を変更しないといけないようです。ググって調べたところ、下記の記事がヒットしました。

TypeScrip + ESLint + Prettier + Jest のプロジェクト設定にハマる - かもメモ

上記を参考に以下の修正を加えました。

env:
  browser: true
  es2021: true
extends:
  - airbnb-base
  - prettier
parser: '@typescript-eslint/parser'
parserOptions:
- ecmaVersion: latest
- sourceType: module
+ project: './tsconfig.json',
plugins:
  - '@typescript-eslint'
rules:
  '@typescript-eslint/strict-boolean-expressions':
    - error
    - allowString: false
      allowNumber: false
      allowNullableObject: false
      allowNullableBoolean: false
      allowNullableString: false
      allowNullableNumber: false
      allowAny: false

再度Lint実行します。

npx eslint index.ts
/path/to/project/index.ts
  14:5  error  Unexpected nullable object value in conditional. An explicit null check is required  @typescript-eslint/strict-boolean-expressions

✖ 1 problem (1 error, 0 warnings)
  1 error and 0 warnings potentially fixable with the `--fix` option.

意図通りLintエラーを出せました。

[おまけ]!= nullとeqeqeqルールの両立

!= null のイディオムはeslintのeqeqeqルールに抵触することが気になる方もいると思います。

その場合、eslint/eqeqeq.md#allow-nullで紹介されているように null のみを例外とすることで == != をtypo入力してしまうことを防ぎつつnull比較のイディオムを使用することができます。

rules:
  eqeqeq:
    - error
    - always
    - null: ignore

参考