この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
吉川@広島です。
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