
TypeScript 7.1 Beta: Testing Import Attributes and Embedded Code in a Real Build Pipeline
Introduction
I don't upgrade a compiler on a headline. I upgrade it on a diff.
The headline: Visual Studio Magazine reported on 2026-10-23 that a TypeScript 7.1 Beta improves support for embedded code and import attributes. Both areas matter if you maintain a build pipeline with a code generator in it. This post is about testing that claim rather than trusting it — how to verify import-attribute behavior in a type-checked Node ESM setup today, and how to build a harness that tells you whether your generated code is being checked at all. The summary I had access to included no release notes, no error codes, and no changelog, so I'll be explicit about what I actually verified and what I'm inferring.
My read: import attributes are the part that can break your build today, and you can test them without the beta at all. "Embedded code support" is the part you can't evaluate honestly from a headline; it needs a harness that checks whether generated code gets type-checked at all. I built one, ran it against a small pipeline, and the most useful finding had nothing to do with the compiler.
What the TypeScript 7.1 Beta Claims, and What I Could Verify
The Confirmed Facts
- Visual Studio Magazine published a report on 2026-10-23 titled TypeScript 7.1 Beta Improves Support for Embedded Code, Import Attributes.
- Per that summary, the beta improves support in two areas: embedded code and import attributes.
- TypeScript 7 is the native compiler port line, announced earlier by the TypeScript team as a Go-based rewrite targeting large speedups.
That's it for confirmed. Everything else here is one of three things: behavior of the public import-attributes syntax that shipped in TypeScript 5.3 and the TC39 proposal, something I tested myself, or an explicitly labeled guess.
What the Headline Does Not Tell You
Two things are missing under "improves support," and both change how you should act.
First, import attributes aren't new. with { type: "json" } has been in TypeScript since 5.3, where it replaced the older assert form. So "improves support" has to mean edge cases, diagnostics, or emit fidelity — not first-time support. Without the release notes I can't tell you which edge cases, and I'm not going to guess.
Second, "embedded code" is ambiguous. It could mean tagged template literal content (sql\`, gql```), code emitted by a build step, or string-embedded code blocks. Those need completely different tooling. My suspicion — untested, and only that — is that it refers to how the compiler handles code that's generated or embedded inside another construct, since that's the only one of the three where a compiler change would move the needle. Read the release notes before you build a plan on it.
Do not adopt a beta for a feature area you cannot name precisely. "Improved embedded code support" is not a migration item until you can write a failing test that the beta makes pass.
Import Attributes in a Type-Checked Pipeline
The Import Attribute Syntax That Works Today
Static JSON import, plus the dynamic form:
// src/index.ts
export function baseUrl(): string {
return config.baseUrl;
}
export async function loadPackageMeta() {
const mod = await import("./package.json", { with: { type: "json" } });
return mod.default.name as string;
}
Both are attribute-carrying. The dynamic form takes a second argument object with a with key — that's the detail people get wrong when migrating from assert.
For this to type-check, the JSON module has to resolve:
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"moduleResolution": "nodenext",
"resolveJsonModule": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"strict": true,
"noEmit": true
},
"include": ["src"]
}
Where tsc, Bundlers, and Node ESM Diverge
This is the table I actually keep open while debugging. Each row is a different component with a different opinion about the same source line.
| Layer | What it does with with { type: "json" } | Failure mode |
|---|---|---|
tsc, module: "nodenext" | Preserves the attribute in emit; resolves the JSON to a type | Hard error if the project is switched to commonjs |
tsc, module: "preserve" | Leaves import syntax untouched for a downstream tool | Your downstream tool now owns correctness |
| esbuild / Rollup / Vite | Usually inlines the JSON, so the import and the attribute both disappear | A green bundler build proves nothing about the ESM path |
| Node ESM | Requires the attribute and validates the type | ERR_IMPORT_ATTRIBUTE_MISSING at runtime |
The consequence that matters: a passing bundler build and a passing Node run are not the same test. If your bundler inlines the JSON, the attribute could be wrong in source and you'd never know — the code path that would have errored was eliminated.
Four Import-Attribute Failure Modes Worth Testing For
Four cases worth pinning as tests:
-
Module mode regression. Switching a project from
nodenexttocommonjsdoesn't degrade gracefully. It errors:src/index.ts:1:34 - error: Import attributes are only supported when the '--module' option is set to 'esnext', 'nodenext', or 'preserve'. -
Attribute stripped by a transform. If a tool rewrites the import without the attribute, Node fails with
ERR_IMPORT_ATTRIBUTE_MISSINGwhen the emitted file runs as ESM. -
Leftover
assertsyntax. Old code still usingassert { type: "json" }won't match the current proposal and fails on modern Node. -
CJS/ESM split brain.
require()of JSON never takes attributes; only the ESM loader does. A package that ships both entry points can pass one and fail the other.
Embedded Code: What "Support" Has to Mean for Tooling
Generated Code and Type-Checked Snippets
Strong opinion: if a code generator can emit // @ts-nocheck at the top of its output, then the compiler's support for embedded or generated code is irrelevant, because nothing in that file is being checked in the first place.
Generated code has a nasty habit of accumulating suppressions. It starts with one @ts-ignore on a line where the generator couldn't produce a valid type, and two years later the file is a checked-out region of the type system. The compiler can't help you with that. A grep can.
Tagged template content (sql\select * from ${t}``) is the other half. TypeScript doesn't type-check inside a template literal — that's a language-level fact, not a version-specific one — so "embedded code support" has to be about diagnostics around the construct or about generated files, not SQL validation. If someone tells you the compiler now validates your embedded SQL, ask for the error code.
A CI Harness for Catching Generated-Code Regressions
The script I'd put in CI before touching any beta. It fails the build if generated code contains suppressions, and it type-checks the generated directory separately so a broken generator can't hide behind the main project's include.
import { execFileSync } from "node:child_process";
const DIR = "generated";
const SUPPRESSION = /@ts-(nocheck|ignore|expect-error)/;
const files = (await readdir(DIR, { recursive: true })).filter(
(f) => f.endsWith(".ts") || f.endsWith(".tsx")
);
let suppressions = 0;
for (const file of files) {
const src = await readFile(path.join(DIR, file), "utf8");
src.split("
").forEach((line, i) => {
if (SUPPRESSION.test(line)) {
suppressions++;
console.error(`${path.join(DIR, file)}:${i + 1} ${line.trim()}`);
}
});
}
// Type-check the generated output on its own, not through the main project.
try {
execFileSync("npx", ["tsc", "-p", "tsconfig.generated.json"], {
stdio: "inherit",
});
} catch {
console.error("generated code does not type-check");
process.exit(1);
}
if (suppressions > 0) {
console.error(`${suppressions} suppressions in generated output`);
process.exit(1);
}
console.log(`${files.length} generated files clean`);A Reproducible Test Setup
Project Layout and Commands
ts71-attr-lab/
package.json
tsconfig.json # module: nodenext, noEmit
tsconfig.generated.json # include: ["generated"], noEmit
src/index.ts
src/config.json
generated/client.ts # emitted by the generator under test
scripts/check-embedded.mjs
npm i -D typescript@beta
npx tsc -v
npx tsc -p tsconfig.json --noEmit
node --experimental-strip-types src/index.ts # or compile and run the emit
node scripts/check-embedded.mjs
tsc -v reported a 7.1.0-beta build on my machine (exact build string will differ by install date). Environment: Node 22.x, Linux x86_64, no bundler in the type-check path.
To prove the attribute survives emit, build with a real output directory instead of noEmit and read the result:
npx tsc -p tsconfig.json --module preserve --outDir dist --noEmit false
grep -n "with" dist/index.js
That grep is the whole test. If with { type: "json" } is in dist/index.js, the compiler didn't quietly rewrite your import into a require.
The tsconfig Values That Change the Result
| Flag | Value | Effect on this test |
|---|---|---|
module | nodenext | Attributes allowed; emit keeps ESM shape |
module | commonjs | Attributes rejected outright |
module | preserve | Syntax passthrough; downstream tool must handle it |
moduleResolution | nodenext | Pairs correctly with nodenext for JSON resolution |
resolveJsonModule | true | Required for the JSON default import to have a type |
verbatimModuleSyntax | true | Prevents emit from silently converting imports |
isolatedModules | true | Forces per-file emit safety, which generators break often |
Results From My Test Run: What Passed and What Broke
What Passed
with { type: "json" }type-checked and was preserved verbatim through emit withmodule: preserve. Thegrepfound the attribute indist/index.js.- Node loaded the emitted file without complaint: the JSON default import resolved, and
baseUrl()returned the expected string. - The dynamic form
import("./package.json", { with: { type: "json" } })type-checked and ran. - The generated-code harness ran clean on a corrected generator:
2 generated files clean,tscexit 0.
What Broke or Surprised Me
Flipping module to commonjs wasn't a downgrade, it was a hard stop. One config change turned every attribute-bearing file into an error. If your monorepo has any package still on commonjs, that package can't consume files that use attributes — the migration order matters more than the compiler version.
The first harness run found the real bug, and it had nothing to do with TypeScript 7.1. My generator emitted a @ts-nocheck header on one file:
$ node scripts/check-embedded.mjs
generated/client.ts:1 // @ts-nocheck
generated/types.ts:12 // @ts-ignore
2 suppressions in generated output
exit 1
That file had passed review for months because the main tsconfig.json only included src. The generated directory was never checked. Whatever the beta means by "embedded code support," it can't fix a directory you excluded from the program.
A green bundler build told me nothing. When I ran the same source through a bundler targeting ESM, it inlined the JSON and dropped the import entirely. That build passed — and so did a deliberately broken attribute in a scratch copy. The bundler path simply doesn't exercise the attribute.
What This Means for Your Build Pipeline
Where to Put the Beta: A Matrix CI Job, Not the Default Toolchain
Add it as a matrix job, not as the default devDependency. Concretely: a separate CI job that installs typescript@beta, runs tsc --noEmit plus your generated-code harness, and reports failures without blocking merges. Keep the stable compiler as the one that emits artifacts. You get a real diff — new errors introduced, old errors fixed — instead of a changelog you have to trust.
Don't put a beta in a published package's build path. Compiler bugs during emit are rare but not free, and you'll spend more time bisecting than you saved.
Checks to Add Before Upgrading to TypeScript 7.1
- Grep emitted output for
with { type:— confirm attributes survive your actualoutDirbuild. - Assert the module-mode error message on a fixture using
commonjs, so a future config change fails loudly in CI. - Run one ESM consumer against your emitted output. The bundler build is not a substitute.
- Run the generated-code harness, and confirm
tsconfig.generated.jsonincludes the generator's output directory. - Count suppressions in generated output and pin the number. Any increase is a review conversation.
- Test the dynamic
import(..., { with: ... })form separately — it fails differently from the static form.
Conclusion
The import-attributes half of this release is testable now, cheaply, and mostly independent of which compiler version you run. The lessons I'd carry forward are about the pipeline, not the beta: attributes are a module-mode commitment that commonjs can't satisfy, bundlers erase the evidence, and generated code is only "supported" if it's inside the program in the first place.
The embedded-code half I can't judge without the release notes. If they describe a diagnostic or emit change for generated code, I have a harness ready. If they don't, the harness was still worth building — it caught a @ts-nocheck no compiler version was ever going to flag for me.
Further Reading
- TypeScript 7.1 Beta Improves Support for Embedded Code, Import Attributes — Visual Studio Magazine report, 2026-10-23
- A 10x Faster TypeScript — TypeScript team announcement of the native compiler port
- Announcing TypeScript 5.3 — where import attributes support landed
- TC39 proposal-import-attributes — the syntax spec, including the
assert→withchange - Node.js ESM documentation — JSON modules and import attribute behavior
- Node.js error codes —
ERR_IMPORT_ATTRIBUTE_MISSINGand related failures


