Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ts-morph と ast-grep でたくさんの TypeScript コードを書き換えた話
Search
Tomoya Chiba
January 12, 2024
Technology
4
4k
ts-morph と ast-grep でたくさんの TypeScript コードを書き換えた話
Tokyoto.js #02 - Kyoto.js in Tokyo (
https://kyotojs.connpass.com/event/302442/
) で発表した資料です。
Tomoya Chiba
January 12, 2024
Tweet
Share
More Decks by Tomoya Chiba
See All by Tomoya Chiba
スポンサーブース用の ruby.wasm くじを vibe coding した話
tomoasleep
0
110
東京Ruby会議12ヘルパー楽しかった✌
tomoasleep
0
140
rbs-inline 生成してみた
tomoasleep
1
330
LiveShare で森羅万象を共同編集する(?)
tomoasleep
1
710
GitHub Actions による RSpec の時間を半分以上短縮した話
tomoasleep
2
1.5k
render 出来るオブジェクトの作り方
tomoasleep
0
250
Rails アプリを10年以上継続していくためのフロントエンドの底上げ
tomoasleep
3
1k
Rails のブラウザテストを Playwright で動かすようにしたらデバッグが簡単になって捗った
tomoasleep
4
3.1k
Sorbetやっていき(たい)宣言
tomoasleep
0
450
Other Decks in Technology
See All in Technology
ソフトウェアエンジニアの生成AI活用と、これから
lycorptech_jp
PRO
0
790
RDS の負荷が高い場合に AWS で取りうる具体策 N 連発/a-series-of-specific-countermeasures-available-on-aws-when-rds-is-under-high-load
emiki
7
4.5k
React19.2のuseEffectEventを追う
maguroalternative
2
580
Biz職でもDifyでできる! 「触らないAIワークフロー」を実現する方法
igarashikana
3
1.3k
映像エッジAIにおけるNode-RED活用事例
emirmatsui
0
120
[VPoE Global Summit] サービスレベル目標による信頼性への投資最適化
satos
0
200
混合雲環境整合異質工作流程工具運行關鍵業務 Job 的經驗分享
yaosiang
0
140
Bill One 開発エンジニア 紹介資料
sansan33
PRO
4
14k
MCP ✖️ Apps SDKを触ってみた
hisuzuya
0
250
CoRL 2025 Survey
harukiabe
1
240
AIフル活用で挑む!空間アプリ開発のリアル
taat
0
130
Introduction to Sansan Meishi Maker Development Engineer
sansan33
PRO
0
310
Featured
See All Featured
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
48
9.7k
Balancing Empowerment & Direction
lara
5
700
RailsConf 2023
tenderlove
30
1.3k
BBQ
matthewcrist
89
9.8k
VelocityConf: Rendering Performance Case Studies
addyosmani
332
24k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
46
7.7k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
3.7k
Art, The Web, and Tiny UX
lynnandtonic
303
21k
Reflections from 52 weeks, 52 projects
jeffersonlam
353
21k
The Illustrated Children's Guide to Kubernetes
chrisshort
49
51k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
127
54k
Side Projects
sachag
455
43k
Transcript
Tomoya Chiba (@tomoasleep) ts-morph と ast-grep でたくさんの TypeScript コ ードを書き変えた話
1
千葉 知也 (@tomoasleep) Qiita 株式会社 エンジニア Tokyo と Nagoya に拠点があります
自分は Tokyo の民です Ruby と TypeScript と HCL と YAML を書いたり消した りする仕事をしています 自己紹介 2
ライブラリ移行 → たくさん書き換えないと行けない → つらい!!無理!! ts-morph と ast-grep を使って ↑
に立ち向かう話 今日のお題 3
移行したもの: GraphQL のコード生成ライブラリ apollo-tooling (Deprecated) → GraphQL Code Generator type
User implements Node { id: ID! username: String! email: String! } query FindUser($id: ID!) { user(id: $id) { id username email } } 今回移行したライブラリ 4
移行したもの: GraphQL のコード生成ライブラリ apollo-tooling (Deprecated) → GraphQL Code Generator //
こんな感じのコードを生成してくれる export interface User = Node & { __typename?: 'User'; id: ID; username: string; email: string; }; export interface GetUser { user: GetUser_user; } export interface GetUser_user { id: string; username: string; email: string; 今回移行したライブラリ 5
移行したもの: GraphQL のコード生成ライブラリ apollo-tooling (Deprecated) → GraphQL Code Generator 今回移行したライブラリ
6
生成されるものがめちゃくちゃ変わってしまう ( ファイル名, export, ...etc) これをほとんどのコードに対して行う必要がある import GetItemQuery from '~/graphql/generated/someQueryByQueryName.gql'
import { GetItem, GetItemVariable, GetItem_items } from '~/graphql/generated/types/someQueryB const { data, refetch } = useQuery<GetItem, GetItemVariables>(GetItemQuery) const items: GetItem_items[] = data.items ↓ import { GetItem, GetItemDataDocument } from '~/graphql/generated/some-query-by-file-name' type GetItem_items = GetItem['items'][number] const { data, refetch } = useQuery(GetItemDocument) const items: GetItem_items[] = data.items 色々書き換える必要が出てくる 7
(VSCode の機能とか頑張って使えばなんとかなるんじゃね?) 結論: 失敗 人がやるとミスが出る 最初の試行錯誤した部分と、その後で書き換え方がブレて一貫性がない 後から発生する要望、フィードバックに答えられない Conflict への対応が重すぎ レビュアーの負担も重い
Try 1: 手で頑張って置き換えてみる 8
ts-morph, ast-grep 等のツールを使う 全ての書き換えをスクリプト化する 手での書き換えは ( 基本) やらない # 書き換えに使ったスクリプト例
( 後のスライドで再掲します) # コードの状態をリセット BASE_COMMIT_ID=$(git log --grep="XXXXXXX" --author="tomo.asleep" -n 1 --pretty=%H) git restore --source=$BASE_COMMIT_ID --worktree -- src/ # コードを書き換え ast-grep --pattern 'useQuery<$$$>($$$P)' --rewrite 'useQuery($$$P)' \ --lang ts --update-all src/ ts-node ./rewriter-with-ts-morph.ts # Linter, Formatter でコードを整形 yarn run format Try 2: 自動化する 9
https://ts-morph.com/ TypeScript ファイルの書き換えを行える AST 操作だけでなく、様々な書き換え機能を提供 VSCode のリファクタリング機能と出来ることは大体行える ( と思う) 公式ドキュメントが分かりやすい
ts-morph 10
import { Project } from "ts-morph"; const project = new
Project(); const myCode = project.getSourceFile("myCode.ts"); // import MyClass from "./file"; を追加 myCode.addImportDeclaration({ defaultImport: "MyClass", moduleSpecifier: "./file", }): // type TypeAlias = string; を追加 myCode.addTypeAlias({ name: "TypeAlias", type: "string", }); await myCode.save(); ts-morph でコードの追加が簡単に行える 11
定義、import した変数とその利用箇所をまとめて rename 出来る // ts-morph で import を書き換える const
importDeclaration = myCode.getImportDeclaration("package") const namedImports = importDeclaration.getNamedImports() namedImports.forEach(namedImport => { // import 名と、利用箇所をまとめて rename する namedImport.setName("NewName") }) /* ----------------------------------------------------------------------------- */ // Before import { OldName } from "package" someFunction(OldName) // ↓ // After import { NewName } from "package" someFunction(NewName) ts-morph で複雑な書き換えが簡単に行える 12
使ってない import の削除も簡単に出来る sourceFile.fixUnusedIdentifiers(); /* ----------------------------------------------------------------------------- */ // Before import
{ OldName, NewName } from "package" someFunction(NewName) // ↓ // After import { NewName } from "package" someFunction(NewName) ts-morph で複雑な書き換えが簡単に行える 13
https://ast-grep.github.io/ AST を加味した grep, sed, awk みたいなもの 様々な言語 (TypeScript, Ruby,
Python, Go, Rust, ...etc) に対応 直感的に書きやすい && 何をやっているかわかりやすい シンプルな書き換えはこちらを使った ast-grep --pattern 'useQuery<$, $>($$$A)' --rewrite 'useQuery($$$A)' \ --lang ts --update-all src/ // Before const data = useQuery<FetchData, FetchDataVariables>(FetchDataQuery) // ↓ // After const data = useQuery(FetchDataQuery) ast-grep 14
コードをリセット → 書き換え → Linter 等で整形、を順に行うスクリプトを書く 冪等になるようにスクリプト化しておくと便利 書き換えの試行錯誤がしやすくなる Conflict 解消、変更への追従が簡単
# コードの状態をリセット BASE_COMMIT_ID=$(git log --grep="XXXXXXX" --author="tomo.asleep" -n 1 --pretty=%H) git restore --source=$BASE_COMMIT_ID --worktree -- src/ # コードを書き換え ast-grep --pattern 'useQuery<$$$>($$$P)' --rewrite 'useQuery($$$P)' \ --lang ts --update-all src/ ts-node ./rewriter-with-ts-morph.ts # Linter, Formatter でコードを整形 yarn run format 書き換えをスクリプト化した 15
書き換えスクリプトは1 日で作れた (※ 個人差があります) ts-morph, ast-grep 公式ドキュメントが充実してて、調べながらやりやすい スクリプト化しておくと色々良かった レビューでの説明がある程度楽 レビューでの指摘を柔軟に取り入れられる
Conflict 解消、変更への追従が簡単で、他の人の作業をブロックしにくい レビュアーに睨まれたりしない やってみてのまとめ 16