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
-
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.
-
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.
-
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.
- Diagnostics —
Program.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
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, andexpressive-code-twoslashto eventually support it.This is a tracking issue, not a bug report. The native
typescript@7package 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, andexpressive-code-twoslashall doimport ts from "typescript"and pass it through astsModule.@ec-ts/twoslash/src/core.ts) and the VFS (@ec-ts/vfs) use the in-process LanguageService:getQuickInfoAtPosition,getCompletionsAtPosition,getSemanticDiagnostics/getSyntacticDiagnostics,getEmitOutput, plusts.sys,ts.optionDeclarations,ts.flattenDiagnosticMessageText,ts.versionMajorMinor, andcreateLanguageServiceover a virtualSystem.There's already some version-aware handling (e.g. dropping
baseUrlfor TS ≥ 6 incore.ts), but TS 7 is a much larger shift than a compiler-option tweak.Blockers with the native
typescript@7packageNo classic entrypoint.
typescript@7.xexports only./package.jsonand./unstable/*— there is no./mainexport, andbinistsconly (notsserver). As a result,import ts from "typescript"throwsERR_PACKAGE_PATH_NOT_EXPORTED, so the packages fail to import (before any of their code runs) when a consumer is on TS 7.Different architecture. The
typescript/unstable/sync(and/async) API is an out-of-process client: anAPIthat spawns/sockets a native process via msgpack IPC, exposingSnapshot/Project/Program/Checker/Emitter. It's asynchronous at its core (the sync variant is aSharedArrayBuffer/Atomicschannel wrapper) and is explicitly markedunstable.Missing the primitives Twoslash depends on:
getQuickInfoAtPositionequivalent.CheckeroffersgetSymbolAtPosition/getTypeAtPosition/typeToString, so hover output would have to be reconstructed, and wouldn't reproduce the classicdisplayParts/ documentation / tags shape one-to-one.showEmit) — no LanguageServicegetEmitOutput; emit is limited toEmitter.printNode(AST → text), not transpile-to-JS of a virtual source file.Program.getSemanticDiagnostics/getSyntacticDiagnosticsexist, but with a differentDiagnostic/ position /DocumentIdentifiermodel than the classic LanguageService output the code currently consumes.@ec-ts/vfs'screateVirtualTypeScriptEnvironmentbuilds an in-memoryLanguageServiceHostover aMap-backedSystemand callscreateLanguageService. The native API has no in-memorySystem/createLanguageService; it uses a snapshot/document (LSP-style) model in a separate process.Rough scope (for whenever this is viable)
@ec-ts/vfstargetingtypescript/unstable/sync(snapshot/document model instead ofcreateLanguageService).Checkerprimitives.twoslash-protocolnode shapes.showEmit(or documenting it as unsupported under TS 7 initially).unstableAPI 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 thetypescriptpeer range (thecatalog:minentry) to exclude v7, and/or emitting a clearer error when a non-classictsModuleis detected. Happy to open that as a separate, self-contained PR if it'd be welcome.References