Slide 1

Slide 1 text

チーム開発における ⾃作ESLintルールの活⽤と戦略 TypeScriptを活⽤した型安全なチーム開発 Sansan 株式会社 Digitization部 Bill One Entry Group &⽥ 和弘

Slide 2

Slide 2 text

!⽥ 和弘 Sansan株式会社 Digitization部 Bill One Entry Group 2021年 Sansan中途⼊社。 以降Bill One Entry Groupとして、請求書のデータ化に向き合う。

Slide 3

Slide 3 text

はじめに (ES)Lint、使っていますか?

Slide 4

Slide 4 text

はじめに (全員の⼿が挙がる予定✋) (ES)Lint、使っていますか?

Slide 5

Slide 5 text

ESLintを使うと⾔っても、さまざまな使い⽅がある。 - 検索したままにとりあえずルールを⼊れてみる - @typescript-eslint - eslint-plugin-react - 強⼒なルールプリセットを使う - eslint-config-airbnb - XO - チームの癖に合ったルールを設定する/外す はじめに

Slide 6

Slide 6 text

- ESLintのルールを「作る」 - ⾃作ESLintを使う前に確認するべき点 - ⾃作ESLintの活⽤と戦略 本⽇話すこと

Slide 7

Slide 7 text

ESLintを「使う」から「作る」へ

Slide 8

Slide 8 text

Lint - コードのパターンによって検証を⾏う - 静的解析である - バグを事前検知する

Slide 9

Slide 9 text

環境変数に現在の環境を記載している。それを利⽤する⽅法。 ルール例 - nodeEnvは内部的にNODE_ENVの値を取得する - 挙動の違いにより、ヒヤリハットが発⽣ × NG - process.env.NODE_ENV ○ OK - nodeEnv()

Slide 10

Slide 10 text

作り⽅ process.env.NODE_EN V ASTを探索し、特定のパターンが現れたらエラーを上げる

Slide 11

Slide 11 text

実装(実際にはもうちょい多いです) MemberExpression(node: MemberExpression & Rule.NodeParentExtension) { const { object: parentNode, property: targetProperty } = node; const { object: parentObject, property: parentProperty } = parentNode; if ( parentObject.name === "process" && parentProperty.name === "env" && targetProperty.name === "NODE_ENV" ) { // エラーレポート } }

Slide 12

Slide 12 text

⾃作ESLintルールを作る前に

Slide 13

Slide 13 text

⾃作ESLintルールを作る前に 1. すでに同じルールが作られていないか

Slide 14

Slide 14 text

- ESLintの持つデフォルトのルールで 266 個 - eslint-plugin-から始まるnpm packagesは 6500 個以上 すでに同じルールが作られていないか ⼤体の場合解決するためのルールは存在する

Slide 15

Slide 15 text

すでに同じルールが作られていないか ファイル名に関するルール ⾃作してみたが、調べてみると存在した。 - eslint-plugin-unicornのルールの1つ - 他にもファイル名に関するLintは多かった × NG - snake_case_file_name.ts - camelCaseFileName.ts ○ OK - kebab-case-file-name.ts

Slide 16

Slide 16 text

⾃作ESLintルールを作る前に 2. ESLint以外で解決できないか?

Slide 17

Slide 17 text

- JavaScriptのエコシステムはかなり成熟している - TypeScriptの型チェックで⼤概は事⾜りる ESLint以外で解決できないか? ハンマーだけを持っていたら全てが釘に⾒える

Slide 18

Slide 18 text

ESLint以外で解決できないか? expressのハンドラの型はデフォルトで⾮常に緩い × NG ○ OK (req: Request) => { req.params // => any } (req: Request) => { req.params // => { userId: string } } 最初はESLintでRequestの型引数を強制しようと思ったが...。

Slide 19

Slide 19 text

ESLint以外で解決できないか? 我々は習慣的にexpressのRequestHandlerを利⽤していた type RequestHandler

= express.RequestHandler

; const handler: RequestHandler = (req) => { req.params // never } より厳しい型のRequestHandlerを⽤意することで解決した。

Slide 20

Slide 20 text

ESLint以外で解決できないか? 加えて@types/express/index.d.ts に下記を記載すると、 RequestHandlerがdeprecated扱いになり誤って使うことはない declare module "Express" { /** @deprecated */ interface RequestHandler {} }

Slide 21

Slide 21 text

⾃作ESLintルールの活⽤と戦略

Slide 22

Slide 22 text

⾃作ESLintルールの活⽤と戦略 - ESLintでなければならないルール - 今まで誰も書いていないルール

Slide 23

Slide 23 text

Lintとは - コードのパターンによって検証を⾏う - 静的解析である - ロジックに関してはテストで回避する - バグを事前検知する - 今まで発⽣したバグや、チーム内で統⼀するべきもの に対して⾏う - フォーマット等例外もある ESLintでなければならないルール

Slide 24

Slide 24 text

今まで誰も書いていないルール 我々のチームではArray#reduceを使うケースが有る。 ⼀⽅でArray#reduceで第⼆引数の値を誤って利⽤してしまう場 合がある。これを早期発⾒したい。 const sum = 0; [1, 2, 3].reduce((acc, val) => { return sum + val; // sumではなくacc }, sum); // => expected: 6, actual: 3

Slide 25

Slide 25 text

今まで誰も書いていないルール Array#reduceは「利⽤してはいけない」というルールはよく ⾒かける。 が、第⼆引数を利⽤してはいけないというルールはなかった ため作った。

Slide 26

Slide 26 text

最も機会があるのは 「チーム内で発⽣したインシデント」 に対するルール ⾃作ESLintルールの活⽤と戦略

Slide 27

Slide 27 text

チーム内で発⽣したインシデント エラーが発⽣した場合、res.sendがされていなかった。 結果動作が想定した挙動にならなかった。 × NG ○ OK const handler = (req, res) => { try { const result = someFunction(); res.send(result); } catch (e) { return; } } const handler = (req, res) => { try { const result = someFunction(); res.send(result); } catch (e) { res.sendStatus(500); } }

Slide 28

Slide 28 text

チーム内で発⽣したインシデント 我々のチームの習慣に合わせてルールを作成。 全てのフローでsendが送られていることを確認している。

Slide 29

Slide 29 text

チーム内特有のバグ・事象は、仕組みとして⽤意しないと、⼈ 間が注意するしか無い。 - 古参メンバーがレビューなどを通して⾒つけるしか無い - 新⼈メンバーはドキュメントやレビューなどを通して知る しか無い 仕組み化できるのであれば、⼩さな落とし⽳でも避けられる。 チーム内で発⽣したインシデント

Slide 30

Slide 30 text

- ESLintのルールを作るという発想を持つ - 作るルールは考える - すでに同じルールが作られていないか - ESLintで本当に解決すべきか? - チーム特有の事象はベスト - ESLintは基本はコードのパターンに対する注意 - インシデント・ヒヤリハットの再発防⽌策として仕組 み化できないか、1つの⼿札として使う まとめ

Slide 31

Slide 31 text

ハンマーを増やす

Slide 32

Slide 32 text

ESLintを⾜がかりに「エコシステムを活⽤する」 - VSCode / Vim / Emacsの拡張・プラグイン - TypeScript Language Service Plugin - 意外に使い所は少ないので使ったことはないけど...。 ハンマーを増やす

Slide 33

Slide 33 text

VSCode拡張紹介 js-teleporter JavaScriptのテストと実装を ⾏き来する。 やってることはディレクトリ 検索なのでJavaScript以外で も使えはする。

Slide 34

Slide 34 text

VSCode拡張紹介 js-test-outline テストケースをリストアップ する。 jest拡張機能にも似たような 機能があるが、そちらは単純 にリストアップするわけでは ない。

Slide 35

Slide 35 text

- ESLintのルールを作るという発想を持つ - 作るルールは考える - すでに同じルールが作られていないか - ESLintで本当に解決すべきか? - チーム特有の事象はベスト - ESLintは基本はコードのパターンに対する注意 - インシデント・ヒヤリハットの再発防⽌策として仕組 み化できないか、1つの⼿札として使う まとめ