Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
ts-morph と ast-grep でたくさんの TypeScript コードを書き換えた話
Search
Tomoya Chiba
January 12, 2024
Technology
4.4k
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ts-morph と ast-grep でたくさんの TypeScript コードを書き換えた話
Tokyoto.js #02 - Kyoto.js in Tokyo (
https://kyotojs.connpass.com/event/302442/
) で発表した資料です。
Tomoya Chiba
January 12, 2024
More Decks by Tomoya Chiba
See All by Tomoya Chiba
Qiitaアーキテクチャの15年史:モノリスRailsから巨大化するサービスの技術負債をどう解消するか
tomoasleep
1
18k
スポンサーブース用の ruby.wasm くじを vibe coding した話
tomoasleep
0
230
東京Ruby会議12ヘルパー楽しかった✌
tomoasleep
0
200
rbs-inline 生成してみた
tomoasleep
1
440
LiveShare で森羅万象を共同編集する(?)
tomoasleep
1
1k
GitHub Actions による RSpec の時間を半分以上短縮した話
tomoasleep
2
1.7k
render 出来るオブジェクトの作り方
tomoasleep
0
340
Rails アプリを10年以上継続していくためのフロントエンドの底上げ
tomoasleep
3
1.1k
Rails のブラウザテストを Playwright で動かすようにしたらデバッグが簡単になって捗った
tomoasleep
4
3.3k
Other Decks in Technology
See All in Technology
Codex概要
ymiya55
0
470
Datadog の学び方 - あるいは、オブザーバビリティを学ぶとは何か
mananyuki
1
500
ai_cording_with_k8s_knowledge.pdf
mochizuki875
1
310
ハードウェアコンペでもAI駆動開発が進んでいる話
iotengineer22
1
510
分析AIエージェントを育てる、動く環境を育てる
codatum
1
390
DWH の限界突破! AI エージェント向け爆速リアルタイムデータ基盤 ClickHouse
jozono
1
300
生成AIを使って「人が」考える技術 ― AI時代の人機共想と実践ノウハウ|UNITT AC2026
ishiirikie
0
570
OSC2026on_the-world-is-waiting-for-your-voice.pdf
naruoga
0
360
自律型 AI をセキュアに実装!Gemini と MIG で作る動的コード実行環境
recruitengineers
PRO
1
230
go-sqletch
moznion
0
110
2026-10-01_MagicPod_QAハーネスエンジニアリングとQA組織の未来像
ynisqa1988
1
560
ビジネスを止めない技術的負債の返済のための戦略とその手法 - 技術的負債と向き合う / Complexity and Simplicity
soudai
PRO
3
670
Featured
See All Featured
Technical Leadership for Architectural Decision Making
baasie
3
600
Being A Developer After 40
akosma
92
590k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
2
470
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
920
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
260
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
440
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.9k
The SEO Collaboration Effect
kristinabergwall1
1
590
ラッコキーワード サービス紹介資料
rakko
1
5.2M
How Software Deployment tools have changed in the past 20 years
geshan
2
35k
Producing Creativity
orderedlist
PRO
348
41k
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