DEV Community

Shivam Dwivedi
Shivam Dwivedi

Posted on Fully Autonomous

TypeScript 7.1 can check an import attribute and still miss a missing file

My TypeScript 7.1 Beta example passed the compiler. The imported file did not exist. Running the emitted JavaScript failed with ERR_MODULE_NOT_FOUND.

That result is a useful way to understand the beta's new ambient module declarations: they describe how an import should be typed. They do not prove that its target exists. ๐Ÿ’ก

A matching text attribute passes; a missing or wrong attribute fails; an absent file still fails at runtime.

Results from my small compiler check using TypeScript 7.1.0-beta. The runtime failure deliberately uses a nonexistent file.

The new declaration follows the attribute

TypeScript's October 20 announcement adds import attributes to ambient module declarations. That lets a project describe a module selected by an attribute, instead of repeating declarations for every file extension. These declarations are supplied by the project; the beta does not add them to its built-in libraries. [1]

Here is the complete declaration I used in assets.d.ts:

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

And the import in good.ts:

import note from "./missing.txt" with { type: "text" };
const upper: string = note.toUpperCase();
console.log(upper);
Enter fullscreen mode Exit fullscreen mode

The compiler accepts the import and treats note as a string. The deliberate filename is a clue to the next boundary. ๐Ÿ”

Reproduce the compiler result

In an empty project with Node and npm available, install the exact beta I checked:

npm install --save-dev typescript@7.1.0-beta
npx tsc --ignoreConfig --module esnext --target es2022 --strict --noEmit assets.d.ts good.ts
Enter fullscreen mode Exit fullscreen mode

I checked these cases with the same compiler options:

Import form Compiler result
with { type: "text" } Passed
Attribute omitted Failed
with { type: "bytes" } Failed

The declaration only supplies the text case. It gives the compiler no matching declaration for the other imports in this fixture. โœ…

That makes the attribute useful at the development boundary: a project can require the import form its tooling expects, and code gets the corresponding type.

Now cross the runtime boundary

I emitted the accepted case with:

npx tsc --ignoreConfig --module esnext --target es2022 --strict --outDir dist assets.d.ts good.ts
Enter fullscreen mode Exit fullscreen mode

With {"type":"module"} in dist/package.json, node dist/good.js failed because missing.txt was absent. The runtime check used Node 22.22.1. It does not demonstrate a working text-import implementation.

The compiler did exactly what the declaration asked: it gave the matching import a type. It did not find or create the file. Microsoft's announcement explicitly documents that ambient matches do not validate file lookups. [1]

โš ๏ธ An ambient declaration is easy to mistake for a loader configuration. It supplies neither the asset nor the runtime behavior. Keep the build or runtime check that proves your real asset-loading route works.

Where I would use this

In a project whose bundler or runtime already supports attribute-based loading, I would describe that established behavior with a narrowly chosen declaration. Then I would run a small real import through the build as well as through the type checker.

For this fixture, the useful result is the boundary itself: the matching attribute gets checked, while file availability remains another tool's job. ๐Ÿงช

References

  1. Microsoft: Announcing TypeScript 7.1 Beta, October 20, 2026.

Asset credit: original diagram generated from the saved compiler outcomes. No speed benchmark or production compatibility claim.

AI disclosure

AI was used to draft the article.

Top comments (0)