ajv: Typescipt and ESM yields "Ajv This expression is not constructable"
I have a TS project in ESM mode (type:module) and getting VSCode errors using certain modules such as Ajv…
Given this in my tsconfig.json:
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "nodenext",
"esModuleInterop": true
// I had a lot more stuff but the top two lines are the ones causing issue (Node16 is same)
}
}
package.json
{
"name": "test",
"type": "module",
"dependencies": {
"ajv": "^8.11.0"
},
"devDependencies": {
"@tsconfig/node16": "^1.0.3",
"@types/node": "^18.11.0",
"typescript": "^4.8.3"
}
}
and finally code:
import Ajv from "ajv"
export const getClient = ():Ajv => {
return new Ajv()
}
I get the error as attached
Note that this code compiles correctly.
If I do:
import Ajv from "ajv"
export const getClient = ():Ajv.default => {
return new Ajv.default()
}
Then error goes away but it does not run.
I think this is something to do with CJS modules needing to add something for esm export but not sure exactly what.
Here’s a sandbox
About this issue
- Original URL
- State: open
- Created 2 years ago
- Reactions: 27
- Comments: 23 (8 by maintainers)
Commits related to this issue
- Apply Ajv TS workaround See: https://github.com/ajv-validator/ajv/issues/2132 — committed to fardjad/node-llmatic by fardjad 9 months ago
- build: properly workaround Ajv TS type definitions bug https://github.com/ajv-validator/ajv/issues/2132 Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com> — committed to SAP/e-mobility-charging-stations-simulator by jerome-benoit 6 months ago
- Add named exports for main classes (#2389) fixes #2381 #2132 Co-authored-by: Jason Ian Green <jasoniangreen@users.noreply.github.com> — committed to ajv-validator/ajv by benasher44 3 months ago
This does run correctly in Node.js, as @nicojs pointed out. I don’t know of any runtime or bundler where it wouldn’t run. However, it’s probably not the API that AJV intended to give to Node.js ESM users.
In the runtime CJS code, AJV uses a clever interop pattern:
that makes
Ajv
exposed both onmodule.exports
andmodule.exports.default
. So in reality, a Node ESM importer can do both of these:However, the types don’t reflect the existence of this interop pattern. The types just say
which implies that only this part exists at runtime:
Which is why TypeScript is only going to let you access the symbol on
module.exports.default
:To make the types more accurate, you should do something like this:
Notice that there’s now an
export = Ajv
instead ofexport default Ajv
, but the namespaceAjv
also has adefault
export/property with the same type.Any updates with version v8?
Same problem here in a ESM package. Here is a temp workaround:
This could easily be fixed by providing named exports for
Ajv
, and then import those:The existing default export can be left untouched to avoid breaking any backwards compatibility. I’d be happy to make a PR for that.
This is also working for me:
(no need for an
unknown
type assertion)It looks like this is still an issue with the latest version. See https://arethetypeswrong.github.io/?p=ajv%408.12.0
Published
@benasher44/ajv
, if folks want to give it a tryWhooops yes it lol 🤦
Draft PR here: #2365
I’m not sure if there is a better way, but I ended up making named exports in two places to make their usages in
export import
easier.My bad - I had the return type on the client creation set to
typeof Ajv
as per VS suggestion which was wrong.