TypeScript 7.1 Beta is the first feature update to the Go-based native compiler that shipped as TypeScript 7.0. It’s getting a lot of attention this month because it brings back a programmatic compiler API, which tools like Vue, Svelte and Astro language tooling, ts-morph and type-aware linters were waiting for. InfoQ reports the team is tentatively aiming for a release candidate and stable build in late November. This guide covers the language features you can use today and shows the new API in action.

Install the Beta

npm install -D typescript@beta
npx tsc --version

Try it on a branch or in CI next to your current version before switching your whole team over.

1. Import Attributes in Ambient Module Declarations

Import attributes tell the runtime or bundler how to interpret an import, for example as JSON, CSS or plain text:

import myData from "./data/someData.json" with { type: "json" };
import someTheme from "./themes/someCoolTheme.css" with { type: "css" };
document.adoptedStyleSheets.push(someTheme);

Until now, projects typed these imports with wildcard declarations like declare module "*.css". That approach ignores the attribute completely. It doesn’t catch a missing with { type: "css" }, and you have to repeat yourself for every extension that behaves the same way. TypeScript 7.1 lets an ambient module declaration match on the import attribute instead:

// global.d.ts (a file with no top-level imports)
declare module "*" with { type: "css" } {
    const content: CSSStyleSheet;
    export default content;
}

declare module "*" with { type: "text" } {
    const content: string;
    export default content;
}

declare module "*" with { type: "bytes" } {
    const content: Uint8Array;
    export default content;
}

Now you get the right type from the attribute, and an error if you forget it:

// ❌ error: missing import attribute
import readme from "../README.md";

// ✅ typed as string
import readme2 from "../README.md" with { type: "text" };

Two things to keep in mind from the official announcement:

  • These declarations aren’t checked against files on disk. A missing file is still reported at runtime or by your bundler.
  • They aren’t included in TypeScript’s built-in libraries. The team recommends that libraries don’t publish them, so app authors should declare them in their own projects.

2. Source Phase Imports

Source phase imports are a proposed ECMAScript feature that adds a source modifier after import. Instead of a running module, you get an object representing the parsed module before it’s linked or executed. For WebAssembly that object is a WebAssembly.Module, which you can pass straight to WebAssembly.instantiate:

import source SuperFastThing from "./super-fast-thing.wasm";
import { WASI } from "node:wasi";

const wasi = new WASI({ version: "preview1" });
const instance = await WebAssembly.instantiate(SuperFastThing, {
    wasi_snapshot_preview1: wasi.wasiImport,
});
wasi.start(instance);

There’s also a dynamic form, and source imports keep their identity across import sites:

const mod = await import.source("./plugin.wasm");

import source a from "./plugin.wasm";
import source b from "./plugin.wasm";
console.log(a === b, b === mod); // true true

Every source import has the new global type AbstractModuleSource. You’ll need --module set to esnext, nodenext or preserve, plus an esnext --lib or --target. The announcement notes that WebAssembly source imports already work in recent Node.js versions, while JavaScript source imports aren’t widely supported yet.

3. –target es2026 Is Now Stable

es2026 is now a stable value for --target and --lib. The important part is the library declarations that moved out of esnext, including Math.sumPrecise(), Iterator.concat(), JSON.rawJSON(), Error.isError(), Map/WeakMap getOrInsert() and getOrInsertComputed(), the Uint8Array base64 and hex helpers, and Array.fromAsync().

{
  "compilerOptions": {
    "target": "es2026",
    "lib": ["es2026", "dom"],
    "module": "nodenext",
    "strict": true
  }
}
// All typed under lib es2026
const total = Math.sumPrecise([0.1, 0.2, 0.3]);

const counts = new Map<string, number>();
for (const word of ["a", "b", "a"]) {
    counts.set(word, counts.getOrInsert(word, 0) + 1);
}

const bytes = new TextEncoder().encode("hello");
console.log(bytes.toBase64(), Uint8Array.fromHex("68656c6c6f"));

declare const someValue: unknown;
if (Error.isError(someValue)) console.error(someValue.message);

A newer target only changes the type declarations TypeScript uses. Your runtime still has to support these methods, so check your Node.js or browser versions before you rely on them in production.

New in –lib esnext

esnext now also includes declarations for Promise.allKeyed and Promise.allSettledKeyed, Iterator.zip and Iterator.zipKeyed, and Iterator.prototype.chunks, join and includes.

4. The New TypeScript 7.1 API

The 7.1 API isn’t one monolithic module. It’s split into focused entry points:

  • typescript/sync: a synchronous compiler client (compiler options, type checker, program APIs)
  • typescript/async: a Promise-based version of the same client
  • typescript/fs: request-scoped full and layered file systems
  • typescript/ast, typescript/ast/is, typescript/ast/factory: AST node types, type guards and node factories

Here’s a minimal “type-check these files” script based on the official example. Unlike tsc, it doesn’t read tsconfig.json.

// check.ts — run with: npx tsx check.ts src/index.ts --strict
import { API, formatDiagnosticsWithColorAndContext, type Diagnostic } from "typescript/sync";

using api = new API();

const report = (d: readonly Diagnostic[]) =>
    console.log(formatDiagnosticsWithColorAndContext(d, api));

const parsed = api.parseCommandLine(process.argv.slice(2));
if (parsed.errors.length > 0) {
    report(parsed.errors);
    process.exit(1);
}

using program = api.createProgram(parsed.fileNames, parsed.options);

const diagnostics = [
    ...program.getGlobalDiagnostics(),
    ...program.getProgramDiagnostics(),
    ...program.getSyntacticDiagnostics(),
    ...program.getSemanticDiagnostics(),
];

report(diagnostics);
process.exit(diagnostics.length > 0 ? 1 : 0);

Why the using keyword?

The compiler runs in Go, so API, Program and snapshot objects hold memory on the Go side. The JavaScript garbage collector can’t free that memory. These objects are Disposable, so either declare them with using, which releases them automatically at the end of the scope, or call api.close() or program.dispose() yourself. If you forget, you’ll leak memory in long-running tools.

Snapshots for editors and watchers

For tools that react to edits, the API has snapshots. You create one with api.createSnapshot(), then derive the next state with snapshot.update() instead of rebuilding everything. Program IDs stay the same across updates, and you can still query the older snapshot. The announcement includes a complete example using the in-memory file system helpers from typescript/fs.

Content mappers

InfoQ’s coverage also highlights content mappers, a supported plugin point that lets a package give TypeScript a virtual view of files like .vue or .svelte, with mappings back to the original source. Mappers run as separate processes and only when you opt in, so treat enabling one like adding any other build plugin.

Takeaway

TypeScript 7.1 Beta is worth testing now. Replace your wildcard *.css/*.txt declarations with attribute-based ones, try --target es2026 if your runtimes support it, and if you maintain tooling, start porting to the new typescript/sync API. Read the full TypeScript 7.1 Beta announcement and the InfoQ write-up for more.