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
tsgolintはいかにしてtypescript-goの非公開APIを呼び出しているのか
Search
syumai
December 05, 2025
Programming
3.1k
9
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
tsgolintはいかにしてtypescript-goの非公開APIを呼び出しているのか
layerx.go #3 の発表資料です!
https://layerx.connpass.com/event/372984/
syumai
December 05, 2025
More Decks by syumai
See All by syumai
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.6k
Oxlintのカスタムルールの現況
syumai
6
1k
Oxlintはいかにしてtsgolintのlint ruleを呼び出しているのか
syumai
2
1.2k
『[入門] Cloudflare Workers』本はなぜ誕生したのか
syumai
0
370
知られているようで知られていない JavaScriptの仕様 4選
syumai
3
1.2k
CloudflareのSandbox SDKを試してみた
syumai
0
850
実践AIチャットボットUI実装入門
syumai
9
4.2k
ProxyによるWindow間RPC機構の構築
syumai
3
1.5k
CloudflareのChat Agent Starter Kitで簡単!AIチャットボット構築
syumai
2
1.2k
Other Decks in Programming
See All in Programming
LLM Plugin for Node-REDの利用方法と開発について
404background
0
160
Contextとはなにか
chiroruxx
0
120
AI駆動開発で崩れていくコードベースを立て直す
kyoko_nr_nr
1
450
Technical Debt: Understanding it Rightly, Engaging it Rightly #LaravelLiveJP
shogogg
0
210
IBM Bobを活用したレガシーアプリの最新化
oniak3ibm
PRO
1
180
代数的データ型って何が嬉しいの? #frontend_phpcon_do
kajitack
8
3.3k
TSKaigi Night Talks 2026_TypeScriptでサプライチェーンの整合性を型に閉じ込める
geekplus_tech
0
330
「エンジニアインターン、どうやって取った?」準備のリアルを語るLT会 Progate BAR
akiomatic
0
120
AIとRubyの静的型付け
ukin0k0
0
550
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
240
決定論的オーケストレーションの設計と実装 / Design and Implementation of Deterministic Orchestration
nrslib
3
1.2k
Java × distroless で 軽量なコンテナイメージを / Java on Distroless
contour_gara
0
510
Featured
See All Featured
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
230
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
270
Navigating Team Friction
lara
192
16k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
360
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Optimising Largest Contentful Paint
csswizardry
37
3.7k
How to Ace a Technical Interview
jacobian
281
24k
Joys of Absence: A Defence of Solitary Play
codingconduct
1
390
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
160
Transcript
tsgolintはいかにしてtypescript-goの 非公開APIを呼び出しているのか syumai layerx.go #3 (2025/12/5)
自己紹介 syumai LayerX所属 ソフトウェアエンジニア ECMAScript 仕様輪読会 / Asakusa.go 主催 Software
Design 2023年12月号から2025年2月号まで Cloudflare Workersの連載をしました Twitter (現𝕏): @__syumai Website: https://syum.ai
本日話すこと typescript-go (tsgo) とは tsgolintとは tsgolintからtsgoの非公開APIへのアクセス方法
typescript-go (tsgo) とは
typescript-go (tsgo) とは TypeScriptのGo実装 もともとTypeScript自身がTypeScriptで実装されている 型チェック、トランスパイルなどのロジックを持つ tsc コマンドを通して処理が行われる 処理の実行はJSにトランスパイルされた上でNode.js上などで行われる microsoft/typescript-go
リポジトリでは、TypeScriptのコアのロジックをGo で再実装している tsc コマンドを置き換える tsgo コマンドを提供 TypeScript 7 としてリリース予定
2025年3月に発表、界隈がざわついた https://devblogs.microsoft.com/typescript/typescript-native-port/
2025年12月に進捗報告があり、かなり安定してきているらしい https://devblogs.microsoft.com/typescript/typescript-native-port/
tsgoの特徴 とにかく速い tscの10倍の速度でビルドが完了することもあるらしい
tsgoの特徴 高い互換性 巨大な型チェックのロジックを持つ checker.ts を行単位でGoにポート checker.ts は5万行ほどのサイズ 巨大すぎて、再設計しての実装は現実的ではない Goが選ばれた理由は、文法的にそのまま移植しやすかったかららしい
tsgoの特徴 ロジックをライブラリ提供しない 型チェックなどのロジックを、tsgoコマンド経由でしか提供しない 実装は、全部 internal package配下にあり、ライブラリ提供を想定しない Launguage Serverや標準出力を使ったIPCベースのAPIサーバーは提供する IPCの仕様はまだ決まってなさそう
None
tsgolintとは
None
tsgolintとは 2025年7月に、typescript-eslintのチームがリリースしたtsgoベースのLinter https://github.com/typescript-eslint/tsgolint 実装は全部Go ESLint + typescript-eslintより20 ~ 40倍速いらしい tsgoのinternal
packageの中身を無理矢理公開して使っている あくまで実験的なプロジェクトで、typescript-eslintの置き換えを意図しない
tsgolintを取り巻く状況 typescript-eslintのチームはもうメンテしていない oxcのチームがforkし、oxlintのtype-aware lintingに使っている こっちはかなりアクティブにメンテされている https://github.com/oxc-project/tsgolint
tsgolintからのtsgoの非公開API呼び出し方法
tsgolintからのtsgoの非公開API呼び出し方法 注: まだ調べ途中なので、不正確な情報があるかもしれません
その1: git submoduleによるtypescript-goの設置
その1: git submoduleによるtypescript-goの設置 git submoduleで typescript-go のリポジトリを丸ごと tsgolint リポジトリ直 下に置いている
go get しないことで自由度を上げている (コード生成、パッチ当てなど) submoduleを明示的に更新しないと壊れない (Go Modulesで管理するとうっかり上がることがあるかもしれない?) . ├── cmd │ └── tsgolint ├── go.mod ├── go.sum ├── go.work ├── go.work.sum ├── internal ├── README.md └── typescript-go
Workspaceでtypescript-go moduleを追加 go.work で typescript-go ディレクトリを指定 github.com/microsoft/typescript-go moduleが相対パスで取得可能に go 1.24.1
use ( . ./typescript-go )
その2: go:linkname
internal packageにlinkするための shim package tsgolint 直下に shim packageを設置 中身は typescript-go
の internal package配下に対応 公開したいものがここに置かれる ./shim ├── ast ├── bundled ├── checker ├── compiler ├── core ├── scanner ├── tsoptions ├── tspath └── vfs
shimファイルの中身 (例) 例は shim/ast/shim.go internalな型をそのままexportしている // Code generated by tools/gen_shims.
DO NOT EDIT. package ast import "github.com/microsoft/typescript-go/internal/ast" /* 中略 */ type AccessExpression = ast.AccessExpression type AccessorDeclaration = ast.AccessorDeclaration
shimファイルの中身 (例) 同じく shim/ast/shim.go の例 関数はbody無しで、 go:linkname でlinkしてexportしている github.com/microsoft/typescript-go/shim/ast/shim/ast.CanHaveDecorators を呼んだら、
github.com/microsoft/typescript-go/internal/ast.CanHaveDecorators のロ ジックが呼ばれる構造 //go:linkname CanHaveDecorators github.com/microsoft/typescript-go/internal/ast.CanHaveDecorators func CanHaveDecorators(node *ast.Node) bool //go:linkname CanHaveIllegalDecorators github.com/microsoft/typescript-go/internal/ast.CanHaveIllegalDecorators func CanHaveIllegalDecorators(node *ast.Node) bool //go:linkname CanHaveIllegalModifiers github.com/microsoft/typescript-go/internal/ast.CanHaveIllegalModifiers func CanHaveIllegalModifiers(node *ast.Node) bool //go:linkname CanHaveModifiers github.com/microsoft/typescript-go/internal/ast.CanHaveModifiers func CanHaveModifiers(node *ast.Node) bool
shimファイルの中身 (例) shim/checker/shim.go の例 非公開メソッドも go:linkname を駆使して公開している type Checker =
checker.Checker //go:linkname Checker_getResolvedSignature github.com/microsoft/typescript-go/internal/checker.(*Checker).getResolvedSignature func Checker_getResolvedSignature(recv *checker.Checker, node *ast.Node, candidatesOutArray *[]*checker.Signature, checkMode checker.CheckMode) *checker.Signature //go:linkname Checker_getTypeOfSymbol github.com/microsoft/typescript-go/internal/checker.(*Checker).getTypeOfSymbol func Checker_getTypeOfSymbol(recv *checker.Checker, symbol *ast.Symbol) *checker.Type わかりやすさのために改行していますが、実際は改行なしです
go:linkname って非推奨じゃなかった? Goのランタイムへの go:linkname が封じられただけで、サードパーティーの moduleに対しては普通に使える runtime packageなどへの go:linkname はNG
github.com/microsoft/typescript-go への go:linkname は何も問題ない 詳しい経緯はANDPADさんの「Go界隈で巻き起こった go:linkname 騒動につい て」を参照ください https://tech.andpad.co.jp/entry/2024/06/20/140000
実際の呼び出し例 internal/rules/no_confusing_void_expression/no_confusing_void_expression.go shimが無いと呼び出せない機能でlintルールを実装 package no_confusing_void_expression import ( "github.com/microsoft/typescript-go/shim/ast" "github.com/microsoft/typescript-go/shim/checker" "github.com/microsoft/typescript-go/shim/scanner"
"github.com/typescript-eslint/tsgolint/internal/rule" "github.com/typescript-eslint/tsgolint/internal/utils" ) /* 中略 */ return utils.Some(callSignatures, func(s *checker.Signature) bool { returnType := checker.Checker_getReturnTypeOfSignature(ctx.TypeChecker, s) return utils.Some(utils.UnionTypeParts(returnType), utils.IsIntrinsicVoidType) })
その3: Go Moduleのreplaceで shim を typescript-go の中に入れる
Go Moduleのreplaceで shim を typescript-go の中に入れる go.mod の例 module github.com/typescript-eslint/tsgolint
go 1.24.1 replace ( github.com/microsoft/typescript-go/shim/ast => ./shim/ast github.com/microsoft/typescript-go/shim/bundled => ./shim/bundled github.com/microsoft/typescript-go/shim/checker => ./shim/checker github.com/microsoft/typescript-go/shim/compiler => ./shim/compiler github.com/microsoft/typescript-go/shim/core => ./shim/core github.com/microsoft/typescript-go/shim/scanner => ./shim/scanner github.com/microsoft/typescript-go/shim/tsoptions => ./shim/tsoptions github.com/microsoft/typescript-go/shim/tspath => ./shim/tspath github.com/microsoft/typescript-go/shim/vfs => ./shim/vfs github.com/microsoft/typescript-go/shim/vfs/cachedvfs => ./shim/vfs/cachedvfs github.com/microsoft/typescript-go/shim/vfs/osvfs => ./shim/vfs/osvfs )
なぜ shim を typescript-go に入れる必要があるのか? 恐らく internal package配下の型情報をexportするのが主目的 shim が
typescript-go 内なので typescript-go の internal が見える go:linkname での関数の公開にも、引数・戻り値の型情報は必要 さっきの shim/ast/shim.go の例 // Code generated by tools/gen_shims. DO NOT EDIT. package ast import "github.com/microsoft/typescript-go/internal/ast" /* 中略 */ type AccessExpression = ast.AccessExpression type AccessorDeclaration = ast.AccessorDeclaration
その4: shim の生成
shim の生成 tools/gen_shims で実装されている ASTを解析して、 internal package配下で公開の型・関数を自動で shim にexport 非公開のメソッド、関数などのexportに使える
extra-shim.json という設 定ファイルもある 手間のかかるexportの作業を全自動化している 実装の詳細は見れてないです
その他のテクニック unsafe.Pointer を駆使して構造体の非公開フィールドを公開してそう やむを得ずtypescript-go本体にパッチを当ててる部分もある様子 patches ディレクトリに入っている
まとめ tsgolintは、あらゆる方法を駆使してtypescript-goの非公開APIを公開している go:linkname Go Modulesのreplace shimの自動生成 どうしても、外部のGo製ライブラリの非公開APIを使いたくなってしまった時に参 考になるかも どう考えても壊れやすい仕組みではあるので、覚悟を決めないと難しそうな 印象
ご清聴ありがとうございました!