Skip to content

Migrate off the internal-package shims when typescript-go exposes a public Go API #13

Description

@domoritz

Background

The TypeScript team published their TS 7.1 API roadmap in
microsoft/TypeScript#63875.
Every item in it targets the JS-facing IPC/snapshot API (createProgram(options),
transpileModule, parseCommandLine/readConfigFile, custom transformers,
NodeHandle<T>, LSP middleware). None of it changes anything for a Go consumer:
the Go functions behind those APIs already exist, they just live in internal/.

The relevant part is in the comments. arthurfiorette asked about a direct Go
compiler API, and jakebailey
replied:

When we move repos, we should be doing that, yeah, though perhaps not
instantly (we need to settle the major version situation)

That carries two things worth tracking here: a public Go API is intended (which
would let us delete shim/ and become go install-able), and the module path
is likely to change (repo move and/or a major-version bump), which breaks our
bump tooling and the shim module-path trick together.

Nothing is actionable upstream yet. This issue records the inventory and the
plan so we can move quickly when it is.

What we depend on today

Ten shimmed packages (~4,300 generated lines), but the generator shims whole
packages — the surface we actually consume is small:

Package Symbols used
ast Node, SourceFile, Symbol, Diagnostic, IdentifierNode, NodeList, Kind + ~80 Kind* constants, NodeFlagsConst/NodeFlagsJSDoc, SymbolFlagsAlias/SymbolFlagsTypeParameter, GetSourceFileOfNode, GetCombinedNodeFlags, GetNextJSDocCommentLocation, ~24 Is* predicates, plus node methods (Symbol(), JSDoc(), TagName(), Text())
checker Checker, Type, TypeToTypeNode, GetTypeAtLocation, ObjectFlagsReference, TypeFlags{StringLiteral,NumberLiteral,Object,UnionOrIntersection,UniqueESSymbol}; methods GetAliasedSymbol, GetConstantValue, GetExportsOfModule, GetExportSpecifierLocalTargetSymbol, GetPropertyOfType, GetSymbolAtLocation, GetTypeAtLocation, TypeToTypeNode
compiler Program, ProgramOptions, CompilerHost, NewProgram, NewCompilerHost; methods BindSourceFiles, GetSyntacticDiagnostics, GetSemanticDiagnostics, GetTypeChecker
tsoptions ParsedCommandLine, NewParsedCommandLine, GetParsedCommandLineOfConfigFile
core CompilerOptions, ScriptTargetES2022, ModuleKindCommonJS, TSTrue/TSFalse/TSUnknown
tspath NormalizePath, ResolvePath, ComparePathsOptions
scanner GetTextOfNode, GetTextOfJSDocComment, GetSourceTextOfNodeFromSourceFile
jsnum Number, FromString
bundled LibPath, WrapFS (embedded lib.*.d.ts)
vfs/osvfs FS

All of that is exported upstream — it is only unreachable because of the
internal/ boundary. Our genuinely-private dependency is five call sites:

Shim symbol Uses Public equivalent upstream?
checker.Checker_getAwaitedType 2 none
checker.Checker_getFullyQualifiedName 2 none
checker.Checker_getTypeArguments 1 yes — (*Checker).GetTypeArguments
checker.Checker_isTypeAssignableTo 2 yes — (*Checker).IsTypeAssignableTo
checker.Type_symbol 2 yes — (*Type).Symbol()

shim/checker/extra-shim.json additionally declares 24 checker methods and four
struct field mirrors (Type, TupleType, Signature, InterfaceType) that
nothing in the repo uses. The field mirrors are our only unsafe.Pointer layout
casts; tools/gen_shims regenerates them from upstream's real struct on every
bump, so they are correct, but they are dead weight.

Bottom line: if internal/checker et al. became importable tomorrow, we would
need exactly two things added to the public surface — getAwaitedType and
getFullyQualifiedName.
Worth raising upstream while the API is being
designed.

Preparation we can do now

  • Replace the three avoidable private accesses with their public
    equivalents (c.GetTypeArguments, c.IsTypeAssignableTo, t.Symbol()),
    then trim the unused ExtraMethods/ExtraFields from
    shim/checker/extra-shim.json. Removes every unsafe cast from the
    generated shim and reduces our private surface to two symbols.
  • Parameterize the typescript-go module path and repo URL in
    tools/bump-tsgo.sh (currently hardcoded in the git ls-remote URL, the
    go list -m query, and the go mod edit -require) and in
    tools/gen_shims/main.go (tsgoInternalPrefix).
  • Ask upstream (on API feature roadmap microsoft/TypeScript#63875 or a follow-up) to export getAwaitedType and
    getFullyQualifiedName on *Checker.

Migration, if packages move out of internal/

The good case — upstream makes the compiler packages importable at
github.com/microsoft/typescript-go/<pkg> (or re-exports them from a public
api/). Then:

  1. Rewrite the 118 imports across 71 files: .../shim/X → the new public path.
  2. Delete shim/ (10 modules) and the 10 replace + require pairs in the
    root go.mod; add one direct require on typescript-go.
  3. Delete tools/gen_shims and simplify tools/bump-tsgo.sh to a plain
    go get; the weekly workflow keeps working unchanged.
  4. Replace the two remaining private calls with whatever public API upstream
    provides (or keep a single minimal shim module just for those).
  5. Drop the "not go install-able" caveats in README.md and AGENTS.md; the
    replace directives are what block installation from the proxy today.
  6. Full fixture oracle (go test ./...) is the acceptance gate, as with any bump.

The bad case — upstream ships a narrow curated Go API modeled on the JS one
(program snapshots, node handles, no direct checker access). That will not cover
TypeToTypeNode with a node builder, awaited/contextual types, type and object
flags, or bundled.LibPath. Evaluate the shape against the inventory above
before assuming migration is possible
; if it does not cover us, the shims stay
and we file a gap report upstream.

Risk: module path change (repo move or major version)

jakebailey's "when we move repos" plus "the major version situation" both point
at the same breakage for us: the module path github.com/microsoft/typescript-go
changing (to a new repo path, or gaining a /v2 suffix).

What breaks:

  • Nothing at build time, immediately. The module proxy serves the pinned
    pseudo-version immutably, so existing builds keep resolving even if the repo
    is archived. No emergency.
  • tools/bump-tsgo.sh fails. go list -m github.com/microsoft/typescript-go@<ref>
    hard-fails once the new location's go.mod declares a different path
    ("module declares its path as X but was required as Y"). The git ls-remote
    against the old URL may keep working via GitHub's rename redirect, which makes
    the failure look confusing — it will be the go list step.
  • The shim module-path trick needs a coordinated rename. Our shim modules are
    named github.com/microsoft/typescript-go/shim/... specifically so that
    importing github.com/microsoft/typescript-go/internal/... satisfies Go's
    internal-package rule. If the upstream path changes, all 10 shim module
    lines, the 10 root replace/require pairs, tsgoInternalPrefix in
    gen_shims, and all 118 import lines must move to the new prefix together, or
    the internal imports stop being legal.

Mitigation is deliberately minimal: parameterize the path in the two tools (see
above) so the change is a two-file edit plus one sed over the imports. We
should not build an internal re-export layer to insulate the import sites —
that is a lot of indirection to save a mechanical rename, and it would hide
which upstream symbols we actually touch.

Watch signals

  • The weekly bump-typescript-go job failing is the primary canary and it
    is already wired: tools/bump-tsgo.sh exits non-zero with "could not resolve
    a typescript/v* release tag" if upstream stops publishing tags at the current
    location, and the go list -m step fails on a declared-path mismatch. A red
    scheduled run means "go read the upstream repo", not "flaky CI".
  • A go.mod module line change, or a /v2+ tag, in microsoft/typescript-go.
  • Any archive/redirect notice on the typescript-go repo, or TS 7 Go sources
    appearing under microsoft/TypeScript.
  • New top-level (non-internal/) Go packages upstream, an api/ package with
    Go doc comments, or new exported wrappers appearing in
    internal/checker/exports.go.
  • Movement on API feature roadmap microsoft/TypeScript#63875 itself, particularly follow-ups to jakebailey's comment or
    a dedicated "public Go API" issue.

🤖 Analysis and issue drafted by Claude Code from
microsoft/TypeScript#63875.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions