TypeScript 7.1 Beta: Testing Import Attributes and Embedded Code in a Real Build Pipeline

TypeScript 7.1 Beta: Testing Import Attributes and Embedded Code in a Real Build Pipeline

pr0h0•
typescriptimport-attributesembedded-codebuild-pipelinenode-esm
AI Usage (76%)

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.

LayerWhat it does with with { type: "json" }Failure mode
tsc, module: "nodenext"Preserves the attribute in emit; resolves the JSON to a typeHard error if the project is switched to commonjs
tsc, module: "preserve"Leaves import syntax untouched for a downstream toolYour downstream tool now owns correctness
esbuild / Rollup / ViteUsually inlines the JSON, so the import and the attribute both disappearA green bundler build proves nothing about the ESM path
Node ESMRequires the attribute and validates the typeERR_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:

  1. Module mode regression. Switching a project from nodenext to commonjs doesn'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'.
    
  2. Attribute stripped by a transform. If a tool rewrites the import without the attribute, Node fails with ERR_IMPORT_ATTRIBUTE_MISSING when the emitted file runs as ESM.

  3. Leftover assert syntax. Old code still using assert { type: "json" } won't match the current proposal and fails on modern Node.

  4. 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.

scripts/check-embedded.mjs
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

FlagValueEffect on this test
modulenodenextAttributes allowed; emit keeps ESM shape
modulecommonjsAttributes rejected outright
modulepreserveSyntax passthrough; downstream tool must handle it
moduleResolutionnodenextPairs correctly with nodenext for JSON resolution
resolveJsonModuletrueRequired for the JSON default import to have a type
verbatimModuleSyntaxtruePrevents emit from silently converting imports
isolatedModulestrueForces 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 with module: preserve. The grep found the attribute in dist/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, tsc exit 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 actual outDir build.
  • 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.json includes 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

Share this post

More posts

Comments