TL;DR
- コーディング規約はドキュメントに書くだけでなく、TypeScript のコンパイラオプションやリンタのルールで強制する。
- 既存のルールで表せない規約は、ESLint の
no-restricted-syntaxで特定の構文を禁止できる。 - この項目は日本語版の訳者によるオリジナルで、本書の各項目に対応する設定を紹介している。
内容
規約を設定で強制する
コーディング規約はドキュメントに書くだけで終わらせず、コンパイラオプションやリンタの設定に反映したほうがよい。一般に問題とされる書き方(型アサーションなど)に例外的な使い道がある場合でも、頻度が高くなければ設定で禁止することを検討する。TypeScript やリンタのエラーは @ts-ignore や @ts-expect-error のコメントで無効にできるので、禁止された書き方をするにはコメントを書かざるを得なくなる(項目43)。これにより、問題のあるコードがうっかり書かれることがなくなり、コードレビューでの見落としも防げる。ただし、やりすぎると @ts-expect-error のコメントだらけになって読みにくくなる。
どこまでエラーにするか迷ったときは、新規プロジェクトなら後から緩めるのは簡単なので、厳しめに設定する。既存のプロジェクトなら、その設定を入れる理由を説明できるかをまず考える。
no-restricted-syntax で独自の規約を書く
規約に合うルールが見つからない場合は、ESLint の no-restricted-syntax を使う。CSS セレクタに似た書き方で構文を指定し、その構文の利用を禁止できる。次の例は、null や undefined を含むユニオン型の型エイリアスを禁止する。
// eslint.config.mjs
export default tseslint.config(
// ...
{
// ...
rules: {
"no-restricted-syntax": [
"error",
{
selector:
"TSTypeAliasDeclaration > " +
".typeAnnotation[type=TSUnionType] > " +
":matches(TSNullKeyword, TSUndefinedKeyword)",
message: "null や undefined を含むユニオン型の型エイリアスを使ってはいけません",
},
],
},
},
)ルールを書くときは typescript-eslint の Playground が便利である。AST も表示できるので、禁止したいコードの AST ノードを確かめながら selector を書き、ルールが期待どおりに動くところまで確認できる。
本書の項目に対応する設定
ルール名は、特に断りがなければ typescript-eslint のものである。
- 項目5:
no-explicit-any、no-unsafe-assignment、no-unsafe-returnでanyの混入を防ぐ。項目43 のanyの拡散も後の 2 つで防げる - 項目9:
consistent-type-assertionsのassertionStyleをneverにして型アサーションを、no-non-null-assertionで非 null アサーションを禁止する - 項目10:
no-wrapper-object-types - 項目13:
consistent-type-definitionsでinterfaceを優先させる - 項目14:
prefer-readonly-parameter-types - 項目16、項目17:
no-restricted-syntaxで制約のないインデックスシグネチャやRecord<string, ...>、Record<number, ...>、数値のキーを禁止する。consistent-indexed-object-styleは書き方を統一するルールで、目的が違う - 項目18:
no-inferrable-types。戻り値の型アノテーションはexplicit-function-return-typeかexplicit-module-boundary-typesで強制する - 項目19: ESLint の
no-shadowでシャドーイングを禁止する - 項目27:
promise-function-async - 項目31: eslint-plugin-jsdoc の
no-types - 項目32: 上の
no-restricted-syntaxの例。条件型の中のundefinedは見逃すが、実用上は十分である - 項目38:
max-params - 項目48:
noUncheckedIndexedAccessとmethod-signature-style - 項目51:
no-unnecessary-type-parameters - 項目52:
unified-signatures - 項目59:
switch-exhaustiveness-checkとnoImplicitReturns。これでneverが必要になるのは、値を返さずswitch以外の分岐を持つ関数だけになる - 項目68: eslint-plugin-jsdoc の
require-jsdocで TSDoc を強制する - 項目72:
enumはno-restricted-syntaxでTSEnumDeclarationを禁止する。パラメータプロパティはparameter-properties、namespaceはno-namespace、トリプルスラッシュインポートはtriple-slash-referenceで禁止する。TypeScript 5.8 で入ったerasableSyntaxOnlyでもenumやnamespaceを禁止できる。実験的デコレータはexperimentalDecoratorsを有効にしなければよい
訳者による解説
本書の内容は主張が強い(opinionated)ので、"strict": true や typescript-eslint の recommended のような一般的な設定だけではカバーできない。また、ソフトウェア開発はチームで行うので、1 人が本書を読んで意識を高めても意味がない。そこで、本書の推奨をコンパイラオプションやリンタで強制する。見逃しがなくなり、後から加わるメンバーにも優しい。ただし、ルールが複雑になるとメンテナンスのコストも高くなる。
キーワード
自分の理解
Effective Rust にも似た項目があった(項目29 Clippyに耳を傾けよう)。