withstudiocms / withstudiocms/expressive-code-twoslash

(Twoslash) Enhancement: Support TypeScript 7 (native Go port) — tracking + blockers

Open
#124 1 comment 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

TRACKER
Dominant language
TypeScript
Stars
31
Forks
8
PR merge metrics
No merged PRs in 30d

Description

Summary

With TypeScript 7 — the native Go port now shipping (typically 8–12× faster type-checking), it would be great for @ec-ts/twoslash, @ec-ts/twoslash-vue, @ec-ts/vfs, and expressive-code-twoslash to eventually support it.

This is a tracking issue, not a bug report. The native typescript@7 package doesn't yet expose the APIs Twoslash relies on, so I've documented the concrete blockers here rather than opening a premature PR. Happy to help implement once the upstream primitives are viable.

Current state

All three entry packages consume the classic TypeScript JS API:

  • @ec-ts/twoslash, @ec-ts/twoslash-vue, and expressive-code-twoslash all do import ts from "typescript" and pass it through as tsModule.
  • The engine (@ec-ts/twoslash/src/core.ts) and the VFS (@ec-ts/vfs) use the in-process LanguageService: getQuickInfoAtPosition, getCompletionsAtPosition, getSemanticDiagnostics / getSyntacticDiagnostics, getEmitOutput, plus ts.sys, ts.optionDeclarations, ts.flattenDiagnosticMessageText, ts.versionMajorMinor, and createLanguageService over a virtual System.

There's already some version-aware handling (e.g. dropping baseUrl for TS ≥ 6 in core.ts), but TS 7 is a much larger shift than a compiler-option tweak.

Blockers with the native typescript@7 package

  1. No classic entrypoint. typescript@7.x exports only ./package.json and ./unstable/* — there is no . / main export, and bin is tsc only (no tsserver). As a result, import ts from "typescript" throws ERR_PACKAGE_PATH_NOT_EXPORTED, so the packages fail to import (before any of their code runs) when a consumer is on TS 7.

  2. Different architecture. The typescript/unstable/sync (and /async) API is an out-of-process client: an API that spawns/sockets a native process via msgpack IPC, exposing Snapshot / Project / Program / Checker / Emitter. It's asynchronous at its core (the sync variant is a SharedArrayBuffer/Atomics channel wrapper) and is explicitly marked unstable.

  3. Missing the primitives Twoslash depends on:

    • Hovers — no getQuickInfoAtPosition equivalent. Checker offers getSymbolAtPosition / getTypeAtPosition / typeToString, so hover output would have to be reconstructed, and wouldn't reproduce the classic displayParts / documentation / tags shape one-to-one.
    • Emit (showEmit) — no LanguageService getEmitOutput; emit is limited to Emitter.printNode (AST → text), not transpile-to-JS of a virtual source file.
    • DiagnosticsProgram.getSemanticDiagnostics / getSyntacticDiagnostics exist, but with a different Diagnostic / position / DocumentIdentifier model than the classic LanguageService output the code currently consumes.
    • VFS model mismatch@ec-ts/vfs's createVirtualTypeScriptEnvironment builds an in-memory LanguageServiceHost over a Map-backed System and calls createLanguageService. The native API has no in-memory System / createLanguageService; it uses a snapshot/document (LSP-style) model in a separate process.

Rough scope (for whenever this is viable)

  • A native-backed environment in @ec-ts/vfs targeting typescript/unstable/sync (snapshot/document model instead of createLanguageService).
  • A hover/quickinfo shim reconstructed from Checker primitives.
  • Diagnostic + position adapters mapping onto the existing twoslash-protocol node shapes.
  • An emit path for showEmit (or documenting it as unsupported under TS 7 initially).
  • Handling the async-native process (sync channel) and the churn of the unstable API surface.

Given the size, this likely wants to wait until the native API stabilizes the LanguageService-style primitives (quickinfo / completions / emit), or ships a compatibility layer.

Interim suggestion (optional, low-risk)

Until then, consumers who happen to be on TS 7 hit a cryptic ERR_PACKAGE_PATH_NOT_EXPORTED. A small guard could improve the experience — e.g. tightening the typescript peer range (the catalog:min entry) to exclude v7, and/or emitting a clearer error when a non-classic tsModule is detected. Happy to open that as a separate, self-contained PR if it'd be welcome.

References

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading @ec-ts/twoslash/src/core.ts and @ec-ts/vfs, then review the current TypeScript LanguageService calls and the typescript/unstable/sync API. This work is blocked by missing native quickinfo, emit, and compatibility primitives. Done would require a viable native-backed VFS plus hover, diagnostic, position, and emit adapters, or a documented unsupported path.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.