Upgrade to Pro — share decks privately, control downloads, hide ads and more …

型情報を用いたLintでコード品質を向上させる

 型情報を用いたLintでコード品質を向上させる

■ イベント
TypeScriptを活用した型安全なチーム開発 2024
https://sansan.connpass.com/event/337268/

■ 発表者
技術本部 Bill One Engineering Unit Core Business APグループ
江川 綾

■ Bill One 開発エンジニア 採用情報
https://media.sansan-engineering.com/billone-engineer

SansanTech

December 22, 2024
Tweet

More Decks by SansanTech

Other Decks in Technology

Transcript

  1. 写真が入ります 江川 綾 Sansan株式会社 技術本部 Bill One Engineering Unit 2021年

    Sansan新卒⼊社。 ⼊社以来、Webアプリケーション開発エンジニアとして インボイス管理サービス「Bill One」の開発に従事。 現在は、Bill Oneにおける機能開発に加えて、 プロダクトの技術的改善に向き合っている。 @erm1116_ @erm1116
  2. アーキテクチャ Email Cloud Load Balancing Backend Cloud Run Database Cloud

    SQL Static Files Cloud Storage Cloud Tasks API Gateway Cloud Load Balancing API Client User Logging Error Reporting Cloud Build Bill One Entry Management / Developer Tools Cloud Functions Monitoring Authentication Auth0 Login Screen Frontend / BFF Cloud Run Pub/Sub
  3. forEach()のコールバックにasync関数を渡している console.logによる出⼒は以下のようになる - “Done” が先に出⼒される - 逐次処理の完了に依存した処理を書くと 期待しない挙動になる - 数値が順不同で出⼒される

    - 順序依存したコードを書くと期待しない挙動になる forEach()は同期関数を期待するので Promiseの解決を待たない。 → async関数を渡した場合は意図しない挙動になりうる (Unhandled Rejectionの発⽣によりプロセスが強制終了する恐れもあるため危険) 8
  4. forEach()のコールバックにasync関数を渡している console.logによる出⼒は以下のようになる - “Done” が先に出⼒される - 逐次処理の完了に依存した処理を書くと 期待しない挙動になる - 数値が順不同で出⼒される

    - 順序依存したコードを書くと期待しない挙動になる forEach()は同期関数を期待するので Promiseの解決を待たない。 → async関数を渡した場合は意図しない挙動になりうる (Unhandled Rejectionの発⽣によりプロセスが強制終了する恐れもあるため危険) 9 チーム開発でどうやって未然に防いでいく?
  5. - Typed Linting について - Rust製 Linter におけるTyped Lintingの現在地 -

    Typed Linting とどう付き合っていくか 今⽇話すこと
  6. - 型情報を⽤いて静的解析を⾏うこと - 多くの Lint ルールは1つのファイルごとに⽣成された 抽象構⽂⽊(AST)をもとに構⽂違反を警告する - このとき、例えば他ファイルからインポートされたコードの情報を理解することはできない -

    Typed Linting では、ASTに加えて型情報を⽤いた解析が可能となる Typed Linting (Type-Aware Linting) とは import { bar } from “bar.ts”; … foo.ts AST-based Linting Typed Linting foo.ts (AST) foo.ts (AST) foo.ts (Type Information) Type Type Type Type Type
  7. - ESLint で TypeScript 対応するためのツールである typescript-eslint を⽤いて⾏うことができる - 設定ファイルで画像のように設定する -

    recommended | strict | stylistic は プロジェクトに合わせて選べばOK - 必要な個別のルールのみ有効にしてもよい - Bill Oneでは⼀部のルールしか有効にしていない - 後からこうした Lint ルールを組み込むのは⼤変なの でプロジェクト⽴ち上げのタイミングでやっておき たい Typed Linting を有効にするには
  8. - 型情報をもとに静的解析できるため、意図しない型が指定されている箇所を特定できる - 例えば、以下は recommended で適⽤されるルールの⼀部 - @typescript-eslint/no-unsafe-* - any型が紛れ込み、型安全性を低下させるようなパターンを防⽌するためのルール

    - @typescript-eslint/unbound-method - thisの束縛によるランタイムエラーを避けるためのルール - @typescript-eslint/no-floating-promises - ハンドルされていない状態のPromiseが⽣まれることを防⽌するためのルール - ほかにも有⽤なルールが提供されている(ルール⼀覧) Typed Linting でできること
  9. Lint の実⾏時間が増加する - 型情報を⽤いて静的解析を⾏う = TypeScript を利⽤した型情報の取得 + ルールの評価 ≒

    tsc によるビルド時間 - 型情報の取得⾃体に時間がかかるため、改善には TypeScript のコンパイル時間の改善に 向き合う必要がある(Performance | TypeScript Wiki) Typed Linting の問題点
  10. - 代表的なものとして、Biome, oxlint などがある - Biome: web開発におけるツールチェインの⼀部としてLinterを提供 - oxlint: JSツールチェインのインフラ(Oxc)としてLinterを提供

    - それぞれのベンチマークによるとESLint に⽐べて Biome は最⼤15倍早く、oxlint は50~100倍速いとされている Rust製 Linter
  11. - 型情報を取得するために TypeScript Compiler (tsserver) を利⽤する場合、 typescript-eslint と同様にパフォーマンスの問題が発⽣する - 更にこのとき、取得できた型情報と

    AST の構造には差分があるため、 AST のマッピング処理が発⽣することになりメンテナンスコストにつながる - 上記より、 Biome や oxlint では、TypeScript Compiler API の部分的な実装として、 ⼀部の型推論に対応することで Typed Linting を実現しようとしている - Biome: https://github.com/biomejs/biome/issues/3187 - oxlint: https://github.com/oxc-project/oxc/issues/3105#issuecomment-2100859582 - が、進捗はあまりなさそうに⾒える Rust製 Linter における Typed Linting
  12. いずれにせよ、現状は Rust製 Linter では 型情報を⽤いたLint は利⽤できない そのため、Typed Linting による安全性の恩恵を加味したうえで Lint

    ツールの選定を⾏う必要がある Rust製 Linter における Typed Linting https://github.com/typescript-eslint/typescript-eslint/issues/10022 (翻訳&要約) 「Typed Linting とはこういうもので、なぜそれを有効にする必 要があるのか」がうまく伝わっていない。 多くのユーザーが Typed Linting を使わず、Biome や oxlintなど に移⾏する理由の⼀端はそこにあると思います。
  13. 現状は他のRust製 Linter で型情報を⽤いた Lint が利⽤できないため、 Typed Linting のためには typescript-eslint を利⽤する必要がある

    → typescript-eslint におけるパフォーマンス問題をどう緩和・改善できるかが重要となる Typed Linting とどう付き合っていくか
  14. 1. typescript-eslint でできるパフォーマンス改善を⾏う 2. 適⽤する typescript-eslint のルールを環境ごとに切り替える 3. Lint ツールを併⽤する

    * ここで紹介している⼿段はあくまでアイディアであり、実施を推奨するものではありません Typed Linting とどう付き合っていくかのアイディア
  15. 1. typescript-eslint でできるパフォーマンス改善を⾏う - Profiling したうえで適⽤するルールの⾒直し - TypeScript のビルド時間の改善 (Performance

    | TypeScript Wiki) - projectService を有効にする - サードパーティのプラグインの設定を⾒直す(Third-Party Plugins) - 注意: 実⾏速度改善のために ESLint のキャッシュを有効にしたい気持ちに駆られるが、 ESLint のキャッシュはファイル単位で⾏われるため、ファイルをまたいだ解析を⾏う Typed Linting との相性が悪く利⽤しないことが推奨とされている Typed Linting とどう付き合っていくかのアイディア
  16. Typed Linting とどう付き合っていくかのアイディア 3. Lint ツールを併⽤する - Typed Linting 以外を

    Biome or oxlint で実⾏することで、並列実⾏による全体実⾏時間の削減 - 厳密にはカスタムルールは ESLint が必要など、細かいルールの使い分けが発⽣する - 全体のボトルネックが型情報の取得の場合はそこまで改善しない可能性もある - 複数 Lint ツールの使い分け・ルール管理により複雑性は増加 - カスタムルールは ESLint が必要など、細かいルールの使い分けが発⽣する - 併⽤に対して、ツールごとに思想が異なっているようにみえる - Biome: ESLint からの“移⾏”を推奨(Migrationコマンド を提供) - oxlint: ESLint との“併⽤”を想定(ルールの競合を防ぐためのプラグインを提供) - 個⼈的には Linter 単体で⾒るとルールのメンテナンスの観点から oxlint の⽅が ベターという考え。⼀⽅ Formatter としても利⽤することを考えると Biome になりそう。