はじめに
TypeScript + ESLint 環境を導入した瞬間、
「警告大量発生」「意味わからん赤線だらけ」
そんな経験、ありませんか?
この記事では、
- eslint の推奨ルールに引っかかりがちな書き方
- その改善方法
を 初心者にもわかりやすく まとめます。
1. ts-ignore を使わない(最後の手段)
NG
// @ts-ignore
const v = riskyValue;
OK
const v: unknown = riskyValue; // 明示的な型
// 型ガード
if (typeof v === 'string') {
// ここから先は v が string だと推論される!
console.log(v.toUpperCase());
} else {
console.error('v is not a string:', v);
}
なぜ?
-
validate 無視はバグ温床
-
修正ポイントがブラックボックス化
@ts-ignore は TypeScript が「あやしいぞ?」と警告してくれている部分を
強制的に黙らせる危険な指示です。なるべく使わないようにしましょう。
どうしても一時的に必要ならコメントを必ず併記します
// TODO: API型定義が更新され次第削除する
// @ts-ignore - ログデータのキーが動的なため
console.log(data["dynamic_key"]);
2. any を避ける(型は必ず定義する)
NG
let data: any;
OK
let data: unknown; // または具体的な型
なぜ?
-
any は型安全の恩恵が消える
-
修正コストが後から爆増する
any はどのような値でも代入可能な型で、
any を使うと TypeScript の最大のメリット(型チェック)が完全に無効化されます。
それに対し、unknown型はどのような値でも代入可能な点は同じですが、
型チェックを行うため、下記のように型が保証されていないとメソッド呼び出しができません。
anyなら下記が通るが、
let value: any = "hello";
value.toFixed(2); // 実行時に落ちても TypeScript は止めない!
unknownでは通らない
let value: unknown = "hello";
value.toFixed(2); // ❌ コンパイルエラー(止めてくれる)
if (typeof value === "number") {
value.toFixed(2); // ⭕ 安全
}
| any | unknown | |
|---|---|---|
| 何でも代入できる | 〇 | 〇 |
| 何でも実行できる | 〇(危険) | ×(安全) |
| 型チェックされるか | × 完全スルー | 〇 必須 |
| バグ防止能力 | 低い | 高い |
3. 待たない非同期関数呼び出しの先頭に void をつける
非同期処理の結果が必要な際は、awaitしているので問題ないですが、結果を待たない場合も待たないことを明示する必要があります。
NG
asyncAction(); // eslint によく怒られる
OK
void asyncAction();
なぜ?
- Promise が返っていることを明示できる
4. dot-notationルールが適用されないキーはブラケット記法(object["key"])を使用する
原則としてドット記法(object.key)が推奨されていますが、javascriptの識別子として無効なキー(予約語やハイフンなど)の場合に限り、ブラケット記法を使います。
逆にドット記法で記述できるものをブラケット記法で書くと警告が出るので気を付けてください。
こちらについては、そもそも構文エラーになります。
以下のような場合です。
NG
obj.default;
OK
obj["default"];
なぜ?
-
eslint(特に dot-notation) ルールで警告対象
-
JavaScript予約語・記号含むキーに対応
obj.key は一見シンプルで便利ですが
すべてのキーに使えるわけではない という落とし穴があります。
具体的には以下です。
エラー or 警告
obj.default; // 予約語
obj.user-name; // ハイフン入り
obj.日本語; // 非推奨
すべてOK
obj["key_name"];
obj["default"];
obj["user-name"];
obj["日本語"];
| ポイント |
|---|
| dot 記法は原則推奨 |
| dot 記法で書けないキーは bracket 記法を使用 |
| bracket 記法も型定義と合わせると安全 |
ESLint の dot-notation は bracket を必要に応じて指示 |
5. 未使用の変数は残さない
NG
const unusedVar = 123;
OK
const usedVar: number = 123;
console.log(usedVar);
なぜ?
-
可読性・保守性が下がる
-
eslint が自動検出し怒られる代表例
6. == ではなく === を使う
NG
if (value == 0) {}
OK
if (value === 0) {}
なぜ?
- 暗黙的な型変換でバグが生まれる
- 意図が読み取り辛くなる
| 演算子 | 説明 | 特徴 |
|---|---|---|
== |
等価演算(値だけ比較) | 型が違っても自動で変換して比較する → 暗黙の型変換あり |
=== |
厳密等価演算(値 + 型比較) | 型も値も同じ場合のみ true → 安全 |
暗黙型変換は言語によって異なり、意図しない挙動を起こしやすいので、多用しない方が良いです。
以前書いた記事を参考に置いておきます。
https://qiita.com/cho-tehu/items/2e099345a5081b2ef793
7. 変数宣言は var ではなく let / const
NG
var x = 1;
OK
const x: number = 1; // 書き換えない値は原則const
なぜ?
- var はブロックスコープにならず予期せぬ動きを起こす可能性がある
var は昔の遺物なので、忘れてください。
var の問題点
-
関数スコープ しか持たない
→ ブロック {} の中でも外で見えてしまう -
再宣言可能
→ 同じ変数名で上書きできてしまう -
巻き上げ(Hoisting) が発生
→ 宣言より前にアクセスできるように見える(バグの温床)
例えばこういうことができます。危険そうでしょ?
if (true) {
var x = 10;
}
console.log(x); // 10 が出力されてしまう。ブロック外でも見える!
8. 未処理の例外をなくす (try/catch or .catch)
NG
fetchData(); // 結果がどうなるかわからない
OK
待たない非同期の時
void fetchData().catch(console.error);
待つ非同期の時
async function getData() {
try {
const data = await fetchData();
console.log(data);
} catch (err) {
console.error(err);
}
}
なぜ?
- 例外未処理は意図しないシステム停止の危険性がある
- eslint の no-floating-promises で警告対象
9. Union型は厳密に扱う
NG
function doSomething(v: string | number) {
console.log(v.toUpperCase()); // numberの場合エラー
}
OK
if (typeof v === "string") {
console.log(v.toUpperCase());
}
なぜ?
- 型安全の基本
TypeScript の Union型(string | number など)は「複数の型のどれかが入る」という意味です。
型ごとにどういった処理を行うか、明確に記述しましょう。
複雑な場合はswitch文で扱うと、見やすいと思います。
switch (typeof v) {
case "string":
console.log(v.toUpperCase());
break;
case "number":
console.log(v + 1);
break;
}
まとめ
| 項目 | 理由 |
|---|---|
ts-ignore禁止 |
自爆装置 |
| 型を定義|anyを避ける | バグを防ぐ |
async呼び出しは void 先頭 |
意図を明示 |
object["key"] を使う |
基本的にはドット記法を推奨 予約語・記号・動的キーでも安全にアクセス可能 |
| 未使用変数撤去 | 可読性up |
=== を使用 |
暗黙変換回避 |
const 優先 |
予期せぬ再代入防止 |
try/catch徹底 |
安全な非同期 |
| 型ガード実施 | union型健全化 |
最後に
開発が始まる前から導入しておき、最初からルールを強制するのがベストですが、
開発が進んでからeslintを導入し、大量に警告が出てしまうというケースは結構あると思います。
そういったとき、数千、数万件の警告が出るとげんなりして、eslintが敵に見えてきますが、eslint は 敵ではなく味方です。
警告はすべて「将来のバグ候補」なので、先に対処してしまった方が、総合的に見て開発コストが減るケースがほとんどだと思います。
チームでルールが強制されていなくても、自ら提案し、導入する。
それが無理な場合も多いと思うので、自分だけでも綺麗なコードを書いておき、自己防衛していきたいですね。