Bug Report
🔎 Search Terms
exports imports field extensions
🕗 Version & Regression Information
This has been the behaviour since TypeScript has had module resolution modes that have supported the exports/imports fields.
💻 Code
// @module: nodenext
// package.json
{
"imports": {
"#*": "./*"
}
}
// a.ts
export function a() {}
// b.ts
// this is allowed, I don't think it should be without `allowImportingTsExtensions`
// since if you replaced the `#` with `./`, it would error and `#` and `./` should mean the same thing here
import { a } from '#a.ts'
a()
🙁 Actual behavior
TypeScript extensions can be used in the exports/imports fields without allowImportingTsExtensions: true. The example above has no errors regardless of allowImportingTsExtensions: true or not. I used patterns in the example since it quite clearly illustrates how it seems like the wrong behaviour since if you replaced the # in the import with ./, it would error which doesn't really make a lot of sense since the imports field here literally means "replace # with ./ but relative to this package.json".
🙂 Expected behavior
TypeScript extensions should only be allowed in the exports/imports fields when allowImportingTsExtensions is enabled.
I'm not at all expecting this to actually be changed since just like #50762, it would totally break the ecosystem since people have packages like this:
// package.json
{
"name": "pkg",
"exports": {
"types": "./types.d.ts",
"default": "./somewhere-else.js"
}
}
// types.d.ts
export declare function a(): void;
// somewhere-else.js
function a() {}
exports.a = a;
but I think this might be a helpful discussion like #50762 since I think the fact that this works is a large part of what makes people have the wrong mental model for how TS finds declaration files, especially when you start talking about dual mode packages.
Imagine if the above wasn't allowed without allowImportingTsExtensions and instead you had to write this package.json:
{
"name": "pkg",
"exports": {
"types": "./types.js",
"default": "./somewhere-else.js"
}
}
It seems very weird to write since there isn't a file at ./types.js but that's really the point. It's not something you should do and using .js makes that clear. And for the case where you have a sibling .d.ts file, having a types condition would look so clearly unnecessary:
{
"name": "pkg",
"exports": {
"types": "./somewhere.js",
"default": "./somewhere.js"
}
}
And for the dual package case where you do it wrong, it's much easier to see why something's wrong than if you were using .d.ts:
{
"name": "pkg",
"exports": {
"types": "./somewhere.js",
"import": "./somewhere.mjs"
"require": "./somewhere.js"
}
}
Bug Report
🔎 Search Terms
exports imports field extensions
🕗 Version & Regression Information
This has been the behaviour since TypeScript has had module resolution modes that have supported the
exports/importsfields.💻 Code
🙁 Actual behavior
TypeScript extensions can be used in the
exports/importsfields withoutallowImportingTsExtensions: true. The example above has no errors regardless ofallowImportingTsExtensions: trueor not. I used patterns in the example since it quite clearly illustrates how it seems like the wrong behaviour since if you replaced the#in the import with./, it would error which doesn't really make a lot of sense since theimportsfield here literally means "replace#with./but relative to this package.json".🙂 Expected behavior
TypeScript extensions should only be allowed in the
exports/importsfields whenallowImportingTsExtensionsis enabled.I'm not at all expecting this to actually be changed since just like #50762, it would totally break the ecosystem since people have packages like this:
but I think this might be a helpful discussion like #50762 since I think the fact that this works is a large part of what makes people have the wrong mental model for how TS finds declaration files, especially when you start talking about dual mode packages.
Imagine if the above wasn't allowed without
allowImportingTsExtensionsand instead you had to write thispackage.json:{ "name": "pkg", "exports": { "types": "./types.js", "default": "./somewhere-else.js" } }It seems very weird to write since there isn't a file at
./types.jsbut that's really the point. It's not something you should do and using.jsmakes that clear. And for the case where you have a sibling.d.tsfile, having atypescondition would look so clearly unnecessary:{ "name": "pkg", "exports": { "types": "./somewhere.js", "default": "./somewhere.js" } }And for the dual package case where you do it wrong, it's much easier to see why something's wrong than if you were using
.d.ts:{ "name": "pkg", "exports": { "types": "./somewhere.js", "import": "./somewhere.mjs" "require": "./somewhere.js" } }