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
3.5k
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会議12ヘルパー楽しかった✌
tomoasleep
0
42
rbs-inline 生成してみた
tomoasleep
1
240
LiveShare で森羅万象を共同編集する(?)
tomoasleep
1
460
GitHub Actions による RSpec の時間を半分以上短縮した話
tomoasleep
2
1.1k
render 出来るオブジェクトの作り方
tomoasleep
0
190
Rails アプリを10年以上継続していくためのフロントエンドの底上げ
tomoasleep
3
870
Rails のブラウザテストを Playwright で動かすようにしたらデバッグが簡単になって捗った
tomoasleep
3
2.8k
Sorbetやっていき(たい)宣言
tomoasleep
0
390
RubyKaigi に貢献したくなる組織の作り方
tomoasleep
1
770
Other Decks in Technology
See All in Technology
オブザーバビリティの観点でみるAWS / AWS from observability perspective
ymotongpoo
7
1k
リーダブルテストコード 〜メンテナンスしやすい テストコードを作成する方法を考える〜 #DevSumi #DevSumiB / Readable test code
nihonbuson
11
5.8k
30分でわかる『アジャイルデータモデリング』
hanon52_
9
2.2k
ビジネスと現場活動をつなぐソフトウェアエンジニアリング~とあるスタートアッププロダクトの成長記録より~
mizunori
0
210
SA Night #2 FinatextのSA思想/SA Night #2 Finatext session
satoshiimai
1
100
個人開発から公式機能へ: PlaywrightとRailsをつなげた3年の軌跡
yusukeiwaki
11
2.7k
急成長する企業で作った、エンジニアが輝ける制度/ 20250214 Rinto Ikenoue
shift_evolve
2
880
10分で紹介するAmazon Bedrock利用時のセキュリティ対策 / 10-minutes introduction to security measures when using Amazon Bedrock
hideakiaoyagi
0
170
目の前の仕事と向き合うことで成長できる - 仕事とスキルを広げる / Every little bit counts
soudai
22
5.8k
Developers Summit 2025 浅野卓也(13-B-7 LegalOn Technologies)
legalontechnologies
PRO
0
150
開発スピードは上がっている…品質はどうする? スピードと品質を両立させるためのプロダクト開発の進め方とは #DevSumi #DevSumiB / Agile And Quality
nihonbuson
1
1.3k
Platform Engineeringは自由のめまい
nwiizo
4
1.9k
Featured
See All Featured
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
99
18k
RailsConf 2023
tenderlove
29
1k
Rails Girls Zürich Keynote
gr2m
94
13k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
29
4.6k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
10
1.3k
Designing for Performance
lara
604
68k
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.6k
Fantastic passwords and where to find them - at NoRuKo
philnash
51
3k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.7k
Reflections from 52 weeks, 52 projects
jeffersonlam
348
20k
Side Projects
sachag
452
42k
What's in a price? How to price your products and services
michaelherold
244
12k
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