TypeScript: `import { default as ... } ...` doesn't work as of 4.8.3
Bug Report
🔎 Search Terms
export default, import default as, namespace, declaration.
🕗 Version & Regression Information
This changed between versions 4.8.2 and 4.8.3.
⏯ Playground Link
I can’t provide a link because both Bug Workbench and CodeSandbox still don’t have 4.8.3.
💻 Code
// @flatten-js/core/index.d.ts
declare namespace Flatten {
class Polygon {
constructor();
}
}
export default Flatten;
// index.ts
import { default as Flatten } from '@flatten-js/core';
const x = new Flatten.Polygon();
🙁 Actual behavior
I get the following error:
error TS2339: Property 'Polygon' does not exist on type ...
When looking at Flatten, it still has a default within, so I need to access Polygon with Flatten.default.Polygon.
🙂 Expected behavior
It gets unwrapped correctly, like in 4.8.2 and before. Right now I’m having to use the following workaround:
import { default as FlattenBad } from '@flatten-js/core';
const Flatten = FlattenBad as unknown as typeof FlattenBad.default;
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 8
- Comments: 18 (7 by maintainers)
Commits related to this issue
- [sql.js] fix export for Node16 and browser scripts Rewrite the export statements so that the package can be used correctly in projects using `module: "Node16"` as well as browser scripts. Previously... — committed to pastelmind/DefinitelyTyped by deleted user 2 years ago
- 🤖 Merge PR #62346 [sql.js] fix export format for Node16 and browser environments by @pastelmind Rewrite the export statements so that the package can be used correctly in projects using `module: "No... — committed to DefinitelyTyped/DefinitelyTyped by pastelmind 2 years ago
- chore: bump typescript to latest and fix compilation issues see https://github.com/microsoft/TypeScript/issues/50690 — committed to pkerschbaum/pkerschbaum-homepage by pkerschbaum 2 years ago
- chore: bump typescript to latest and fix compilation issues see https://github.com/microsoft/TypeScript/issues/50690 — committed to pkerschbaum/pkerschbaum-homepage by pkerschbaum 2 years ago
Chiming in here - while I understand that the old behavior working correctly may have been unintentional, this change in 4.8.3 led to a bunch of breaking changes in my code, which doesn’t seem right for a point release.
The
{ default as X }clause is useful in working around issues with types when you have an ESMnodenextapp and import a RequireJS dependency with a default export whose types haven’t yet been updated fornodenextsupport as suggested in #48845.For example, see the ioredis ESM import snippet (which was correct as of 4.8.2 but no longer works): https://github.com/luin/ioredis#basic-usage
This worked in 4.8.2:
but is broken in 4.8.3, needing to be rewritten as:
to compile. I had to make a bunch of similar changes to fastify plugin imports as well. It looks like ajv is affected as well (https://github.com/ajv-validator/ajv/issues/2047), and likely many other packages.
This seems like a pretty major non-backwards-compatible change for a point release.
This works for me… 🤷♂️
@weswigham, please double check my explanation 😅
@dkulchenko yeah, I understand. We didn’t realize that the bug was being used as a workaround for broken typings. Knowing what I know now, I probably would have held the fix from 4.8.3, but the proverbial toothpaste is out of the tube now.
One correction, though—the typings that are broken were always wrong; they didn’t just need to be updated for
nodenext. The problem is just more observable innodenext. Hopefully the problem libraries will get updated quickly.TL;DR: the library’s types are wrong.
@flatten-js/core is a CommonJS module (it ships some ESM files, but these are not exposed through any standard package.json fields). This means when you do a default import of it (or a
{ default as Whatever }import) in Node, you get the wholemodule.exportsobject. @flatten-js/core’smodule.exportsis an object with roughly the same shape asFlatten, including a member nameddefaultwhose value isFlatten.The way you represent this in a
.d.tsfile is something like this:But instead, as you noted, the
.d.tsfile included saysexport default Flatten. This is not the same thing. It is, admittedly, really puzzling to try to figure out whatexport defaultmeans when you know from the file extension (.d.ts) and package.jsontype(omitted entirely) that you are looking at a CommonJS module, not an ES module. But we do our best and say that it represents something likemodule.exports.default = Flatten. Which is something that the JS file actually has, which is great, but it’s also woefully incomplete. It’s missing every other export besidesdefault, which is why, when you import it, TypeScript shows you that it has adefaultand nothing else. That’s exactly what the typings say.Now, you may object, saying that obviously we should just resolve to the
defaultmember of the module record here. But that would be incorrect. Consider this perfectly correct JS/.d.ts pair:What should we say the type of
libis when you import it?By your hypothetical objection, it would look like
libshould be"default". But in fact, in Node, it’s an object containing propertiesfooanddefault. This is different from what runtimes/transpilers/bundlers who care about that__esModuleflag would give you, so it’s quite important we get this right and warn you that Node is going to return the whole module record.The author of @flatten-js/core was clever to make
module.exportsvirtually indistinguishable frommodule.exports.default—it means that consumers will see the same API regardless of whether their module resolver cares about__esModule. Unfortunately, that same clever duplication was not copied into the type declaration file.The fact that
{ default as Flatten }worked was a bug, and was causing problems for libraries that are typed correctly: https://github.com/microsoft/TypeScript/issues/49567.@adamburgess Sure, thanks. My tsconfig.json:
My issue is probably the same with others with
export {default as XYZ} from './abcd'When I’m trying to import the component in other files using the name given with this current version of typescript I cannot make it work because I get an error telling
Cannot read properties of undefined (reading 'XYZ').It seems like they’re trying to do a UMD module