--moduleResolution minimal
by andrewbranch · Pull Request #50153 · microsoft/TypeScript (original) (raw)
--moduleResolution minimal
is a new module resolution mode that only allows relative paths with file extensions in module specifiers. This means no node_modules
, no dropping extensions, no special meaning for files named index.js
. This mode is suitable (though incomplete, without support for HTTP imports and import maps) for browser-native ESM. It’s also the common denominator in resolution features between the browser, Node, Deno, and bundlers (they don’t agree on much, so not much is included here).
Importing from ./node_modules
not allowed
I’ve decided to disallow relative imports into node_modules at least for now. The problem is that if you have
import * as Three from "./node_modules/three/src/Three.js";
it would be safe, correct, and useful for us to type that module by pulling types from ./node_modules/@types/three/src/Three.d.ts
(which is not handled by any existing behavior—only sibling .d.ts
files are looked up for relative imports). But it’s not clear that such a mapping is always correct or even possible. If a library uses typesVersions
or exports
, the types may be nested in a subfolder. If those options don’t include a wildcard mapping, then it’s impossible to import arbitrary files from the package in Node-style resolution, which makes any attempt to infer a mapping from an arbitrary relative import of an implementation file to its type definition in some other folder feel highly suspect and fragile. The more you think about it, the more it feels like looking at package.json files or trying @types
violates the expectations for relative imports and one of the core assumptions of this resolution mode: that node_modules isn’t special.
We could decide to truly give node_modules no special behavior, perhaps with the one concession that (non-type-only) imports from node_modules/@types
are not allowed. For now I’ve chosen not to block resolution, but to issue an error in the checker when a relative import includes /node_modules/
in its module specifier. This keeps options open for us in the future.
Folks using this for the browser probably will need a way to specify typings for their vendored dependencies, which I assume they will drop in a folder not called node_modules. #50600 tracks this feature request (and it need not be specific to --moduleResolution minimal
).
Importing "*.ts"
allowed
Also included is a new compiler option allowImportingTsExtensions
, which can be enabled only in --moduleResolution minimal
(though the planned --moduleResolution hybrid
will support it as well) and when --noEmit
or --emitDeclarationOnly
is set. When these conditions are met, imports will be allowed to use the extension of the target input file (.ts
, .tsx
, .mts
) rather than the output extension (.js
, .mjs
). Additionally, type-only imports under these options are allowed to use a .d.ts
suffix when resolving to a .d.ts
file. To help with portability, declaration files themselves are not subject to the --allowImportingTsExtensions
and --noEmit
requirements.
// ok as long as
// - moduleResolution
is minimal
// - allowImportingTsExtensions
is set
// - noEmit
or emitDeclarationOnly
is set
// - the specified files exist
import {} from "./a.ts"
import type {} from "./types.d.ts"
URL handling not implemented yet
One thing that’s missing here is URL decoding. However, this is also missing from node16
and nodenext
in ESM mode, and I think it would be a lot of noise to introduce it here in the same PR. I’d like to do that in a follow-up PR.