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.7k
Oxlintのカスタムルールの現況
syumai
6
1.2k
Oxlintはいかにしてtsgolintのlint ruleを呼び出しているのか
syumai
2
1.3k
『[入門] Cloudflare Workers』本はなぜ誕生したのか
syumai
0
440
知られているようで知られていない JavaScriptの仕様 4選
syumai
3
1.3k
CloudflareのSandbox SDKを試してみた
syumai
0
920
実践AIチャットボットUI実装入門
syumai
9
4.3k
ProxyによるWindow間RPC機構の構築
syumai
3
1.5k
CloudflareのChat Agent Starter Kitで簡単!AIチャットボット構築
syumai
2
1.3k
Other Decks in Programming
See All in Programming
LLMによるContent Moderationの本番運用の裏側と品質担保への挑戦
suikabar
3
850
ソフトウェア設計に溶けるインフラ ― AWS CDK のインフラ認識論
konokenj
2
380
【SRE NEXT 2026 Lunch Session】一人目専任SREの立ち上げを加速する ― AIと進めたオンボーディングで2分を0.04秒にした話
pkshadeck
PRO
0
2.5k
20260623_Loop Engineeringで自分の分身の問い合わせBotを作る
ryugen04
0
210
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
160
エンジニア向け会社紹介/Findy Company Profile
findyinc
6
360k
Terraform標準の組織で AWS CDKをどう使うか
mu7889yoon
0
110
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
12
4.8k
コーディングルールの鮮度を保ちたい for SRE NEXT 2026 / keep-fresh-go-internal-conventions-sre-next-2026
handlename
0
140
LaravelLive Japan の裏方のすべて — 第188回 PHP勉強会@東京 (2026-06-24)
suguruooki
2
150
Laravel Boostに学ぶ、AIにPHPを書かせる技術 〜OSSの実装から蒸留するエージェント制御の王道〜
kentaroutakeda
3
330
AWS CDK を「作」ってみた 〜フルスクラッチで見えた CDK の裏側〜 / aws-cdk-from-scratch
gotok365
3
300
Featured
See All Featured
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
220
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2.1k
Raft: Consensus for Rubyists
vanstee
141
7.6k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
260
Side Projects
sachag
455
43k
Statistics for Hackers
jakevdp
799
230k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
220
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
330
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.4k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
23k
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を使いたくなってしまった時に参 考になるかも どう考えても壊れやすい仕組みではあるので、覚悟を決めないと難しそうな 印象
ご清聴ありがとうございました!