Oxlint の型対応プレビュー
本投稿では、型対応リントの技術的プレビューを発表します。 統合性とルールカバレッジが向上した最新のアルファ版については、型対応リントのアルファ版発表 をご確認ください。
oxlint における型対応リントの登場をお知らせできることを大変嬉しく思います!
待ち望んでいた no-floating-promises および関連するルールがついに登場しました。
このプレビューリリースは、意思決定プロセスと技術的詳細を文書化することで、コミュニティとの協力と議論を促進することを目的としています。
クイックスタート
oxlint が既に設定済みの場合、oxlint-tsgolint をインストールし、--type-aware フラグを付けて oxlint を実行してください:
pnpm add -D oxlint-tsgolint@latest
pnpm dlx oxlint --type-awareoxlint がまだ設定されていないが、no-floating-promises を実際に試したい場合:
pnpm add -D oxlint-tsgolint@latest
pnpm dlx oxlint@latest --type-aware -A all -D typescript/no-floating-promises以下のようになります(例):
× typescript-eslint(no-floating-promises): プロミスは待機するか、.catch の呼び出しで終了するか、失敗ハンドラを持つ .then で終了するか、または `void` 演算子で明示的に無視されたものでなければなりません。
╭─[packages/rolldown/src/api/watch/watcher.ts:30:7]
29 │ await this.close();
30 │ originClose();
· ──────────────
31 │ };
╰────その他の構成オプションについては、使い方ガイド をご参照ください。
パフォーマンス
テスト結果によると、以前 typescript-eslint で1分かかっていたリポジトリが、現在では10秒未満で完了するようになりました。
これは、10倍速いTypeScript を実装した Go言語による TypeScript を活用することで達成されています。
oxc-ecosystem-ci からのプロジェクトを用いた測定結果:
| プロジェクト | ファイル数 | 時間 |
|---|---|---|
| napi-rs | 144 | 1.0s |
| preact | 245 | 2.7s |
| rolldown | 314 | 1.5s |
| bluesky | 1152 | 7.0s |
型対応リント
エコシステム内での型対応リントの現状について理解するために、「RustベースのJavaScriptリントツール:高速だが、現時点では型対応リントなし」 を参照してください。
技術的詳細
この新機能の中心となるのは oxc-project/tsgolint です。
tsgolint プロジェクトは当初、typescript-eslint/tsgolint としてプロトタイプ化されました。しかし、typescript-eslint チームは、今後も ESLint での型対応リント開発を継続する予定であるため、このプロトタイプへの開発リソースの割り当てをしないことになりました。
@boshen は @auvred に、oxlint 向けに簡略化されたフォーク版の開発を依頼しました。このバージョンは、フルリントツールに必要な高度な構成解決機能を持たず、型対応ルールのみを含むものです。
@auvred は、このプロジェクトの開発を Oxc 組織下で継続することに快く同意してくれました。
アーキテクチャ
oxlint(Rustで記述)と tsgolint(Go言語で記述)はそれぞれ個別のバイナリとしてコンパイルされます。
oxlint は tsgolint の「フロントエンド」として機能し、CLI処理、パス走査、無視ロジック、診断出力などを担当します。
tsgolint は oxlint のバックエンドとして機能し、パスと構成を入力として受け取り、構造化された診断を出力します。
これによりシンプルなパイプラインが構成されます:
oxlint CLI(パス + ルール + 構成を返す)
-> tsgolint(診断を返す)
-> oxlint CLI(診断を出力する)tsgolint
tsgolint は公開APIを通じて typescript-go と通信しません。
代わりに、内部APIをシムすることでそれらを公開しています。
すべての型対応ルールは、このシムされたAPIに対して直接書かれています。
内部的なアクセスとしては推奨されませんが、動作はしています!
決定プロセス
自前で型チェッカーを実装する
過去に失敗に終わった型チェッカーの実装試みには、以下が含まれます:
- 私自身の型推論の実装試み
- @kaleidawave による ezno型チェッカー の 統合
- @kdy1 による stc
- 他にも多くのコミュニティ内の試みがありましたが、深くまで進んでいませんでした。
また、独自の型推論実装を持つ進行中の Biome 2.0 も存在します。
私たちが自前で型推論器や型チェッカーを実装することは現実的ではないと考えた理由は、常に変化するターゲットである TypeScript に対応しつつ維持することが困難だからです。
TypeScript コンパイラとの通信
typescript-go の導入以前、プロジェクトは、その公開APIにプラグインインターフェースを追加するために、いずれかの方法で AST を estree にマッピングするか、直接タイプスクリプトの AST を走査していました。代表的な例には以下があります:
また、oxlint とのプロセス間通信の検討も行いましたが、このアイデアは放棄しました。
typescript-go では、マイクロソフトのチームは、タイプスクリプトのASTをエンコードし、JavaScript側でデコードする 方向性に傾いています。
これらのアプローチは動作しますが、依然として以下のような問題があります:
oxlintのパフォーマンス特性に合わない、程度の異なるパフォーマンス課題。- タイプスクリプトのASTから
estreeへのマッピングを維持するコスト。
考慮事項
tsgolint はパフォーマンス問題を解決していますが、他にも技術的課題が残っています。
別のTypeScriptバージョンが必要
typescript-go のスナップショットバージョンをリリースし、バージョン番号をタイプスクリプトと一致させる予定です。そうすれば、適切なタイプスクリプトバージョンを持つ oxlint-typescript をインストールできます。
このアプローチの欠点は、oxlint-tsgolint が変更を要求した場合、タイプスクリプトのアップグレードが必要になる可能性があることです。
tsgolint のメンテナンスコスト
タイプスクリプトの内部APIをシムすることは多少リスクを伴います。しかし、タイプスクリプトのASTとそのビジターは実際非常に安定しています。私たちはこのリスクを受け入れ、typescript-go のアップグレード時に破壊的変更を修正します。
typescript-go のバージョンは毎日同期されています。
パフォーマンス問題
tsgolint は、数百のプロジェクトや大量のプロジェクト参照を含む大きなモノレポでは、性能が十分に発揮できません。
バグが発生すると、デッドロックに陥るか、メモリ不足(OOM)を引き起こす可能性があります。
私たちはこれらの問題に対処中であり、typescript-go へのプロファイリングと改善提案を進めています。これにより、すべての typescript-go ユーザーが恩恵を受けます。
コアチームメンバー @camc314 は、すでにいくつかの プルリクエスト を提出し、複数のコードパスを大幅に高速化しています。
v1.0リリース
tsgolint v1.0 に向けて、以下の対応を予定しています:
- 大きなモノレポのパフォーマンス問題
- 個別ルールの構成可能化
- 各ルールの正確性
- IDEサポート
- 全体的な安定性
謝辞
以下の方々に感謝申し上げます:
typescript-goの作成に貢献したタイプスクリプトチーム- 慶賛的な支援を提供してくれた
typescript-eslintチーム tsgolintの作成に貢献した @auvredoxlint+tsgolintの統合に貢献した @camchenry- パフォーマンス問題に関する作業に貢献した @camc314
カンペーンに参加しよう
oxlint と型対応リントに対するフィードバックをお聞かせいただけると嬉しいです。開発ワークフローの改善に役立つことを楽しみにしています。
私たちとつながろう:
- Discord: 実時間での議論のために、コミュニティサーバー へ参加
- GitHub: GitHub Discussions でフィードバックを共有
- イシューページ:
oxlintのバグは oxc に、型対応リントのバグは tsgolint に報告してください。
次のステップ
oxlint をインストール:
pnpm add -D oxlint@latest oxlint-tsgolint@latest
pnpm dlx oxlint --init # .oxlintrc.json を生成または、インストールガイド を参照してください。
--type-aware CLIフラグを使用します。
pnpm dlx oxlint --type-aware.oxlintrc.json 内の任意の型対応ルールで遊んでみてください:
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"rules": {
"typescript/await-thenable": "error",
"typescript/no-array-delete": "error",
"typescript/no-base-to-string": "error",
"typescript/no-confusing-void-expression": "error",
"typescript/no-duplicate-type-constituents": "error",
"typescript/no-floating-promises": "error",
"typescript/no-for-in-array": "error",
"typescript/no-implied-eval": "error",
"typescript/no-meaningless-void-operator": "error",
"typescript/no-misused-promises": "error",
"typescript/no-misused-spread": "error",
"typescript/no-mixed-enums": "error",
"typescript/no-redundant-type-constituents": "error",
"typescript/no-unnecessary-boolean-literal-compare": "error",
"typescript/no-unnecessary-template-expression": "error",
"typescript/no-unnecessary-type-arguments": "error",
"typescript/no-unnecessary-type-assertion": "error",
"typescript/no-unsafe-argument": "error",
"typescript/no-unsafe-assignment": "error",
"typescript/no-unsafe-call": "error",
"typescript/no-unsafe-enum-comparison": "error",
"typescript/no-unsafe-member-access": "error",
"typescript/no-unsafe-return": "error",
"typescript/no-unsafe-type-assertion": "error",
"typescript/no-unsafe-unary-minus": "error",
"typescript/non-nullable-type-assertion-style": "error",
"typescript/only-throw-error": "error",
"typescript/prefer-promise-reject-errors": "error",
"typescript/prefer-reduce-type-parameter": "error",
"typescript/prefer-return-this-type": "error",
"typescript/promise-function-async": "error",
"typescript/related-getter-setter-pairs": "error",
"typescript/require-array-sort-compare": "error",
"typescript/require-await": "error",
"typescript/restrict-plus-operands": "error",
"typescript/restrict-template-expressions": "error",
"typescript/return-await": "error",
"typescript/switch-exhaustiveness-check": "error",
"typescript/unbound-method": "error",
"typescript/use-unknown-in-catch-callback-variable": "error"
}
}



