-
-
Notifications
You must be signed in to change notification settings - Fork 878
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Typescipt and ESM yields "Ajv This expression is not constructable" #2132
Comments
Same problem here in a ESM package. Here is a temp workaround: -import Ajv from "ajv";
+import _Ajv from "ajv";
+
+const Ajv = _Ajv as unknown as typeof _Ajv.default;
const ajv = new Ajv(); |
@kachkaev that now gives me errors about |
Hmm not sure why that would be. I’m running an experiment in blockprotocol/blockprotocol#709, things seem to work fine there. Well, if I’ve got |
My bad - I had the return type on the client creation set to |
This is also working for me: import ajvModule from 'ajv';
const Ajv = ajvModule.default;
const ajv = new Ajv(); (no need for an |
It looks like this is still an issue with the latest version. See https://arethetypeswrong.github.io/?p=ajv%408.12.0 |
@nicojs Did that work at runtime? Its not the same as what @kachkaev wrote, which is just adjusting the types. You're actually assigning to default, rather than just forcing the type inference, which the link provided by @benasher44 indicates this is incorrect. |
I'm always using |
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: module.exports = exports = Ajv;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = Ajv; that makes // In Node, a default import always binds to the
// `module.exports` of a CJS module!
import Ajv from "ajv";
new Ajv(); // module.exports -> Ajv
new Ajv.default(); // module.exports.default -> Ajv However, the types don’t reflect the existence of this interop pattern. The types just say export default Ajv; which implies that only this part exists at runtime: Object.defineProperty(exports, "__esModule", { value: true });
exports.default = Ajv; Which is why TypeScript is only going to let you access the symbol on import Ajv from "ajv";
new Ajv();
// ^^^ The only thing you told me about the `module.exports` of "ajv"
// is that it has a `default` property with an `Ajv` class.
new Ajv.default();
// Yeah, now you’re constructing the class that you declared to exist. To make the types more accurate, you should do something like this: import AjvCore from "./core";
import { Format, FormatDefinition, AsyncFormatDefinition, KeywordDefinition, KeywordErrorDefinition, CodeKeywordDefinition, MacroKeywordDefinition, FuncKeywordDefinition, Vocabulary, Schema, SchemaObject, AnySchemaObject, AsyncSchema, AnySchema, ValidateFunction, AsyncValidateFunction, SchemaValidateFunction, ErrorObject, ErrorNoParams, } from "./types";
import { Plugin, Options, CodeOptions, InstanceOptions, Logger, ErrorsTextOptions } from "./core";
import { SchemaCxt, SchemaObjCxt } from "./compile";
import { KeywordCxt } from "./compile/validate";
import { DefinedError } from "./vocabularies/errors";
import { JSONType } from "./compile/rules";
import { JSONSchemaType } from "./types/json-schema";
import { _, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions } from "./compile/codegen";
import { default as ValidationError } from "./runtime/validation_error";
import { default as MissingRefError } from "./compile/ref_error";
declare namespace Ajv {
const _default: typeof Ajv;
export { _default as default, Format, FormatDefinition, AsyncFormatDefinition, KeywordDefinition, KeywordErrorDefinition, CodeKeywordDefinition, MacroKeywordDefinition, FuncKeywordDefinition, Vocabulary, Schema, SchemaObject, AnySchemaObject, AsyncSchema, AnySchema, ValidateFunction, AsyncValidateFunction, SchemaValidateFunction, ErrorObject, ErrorNoParams, Plugin, Options, CodeOptions, InstanceOptions, Logger, ErrorsTextOptions, SchemaCxt, SchemaObjCxt, KeywordCxt, DefinedError, JSONType, JSONSchemaType, _, str, stringify, nil, Name, Code, CodeGen, CodeGenOptions, ValidationError, MissingRefError };
}
declare class Ajv extends AjvCore {
_addVocabularies(): void;
_addDefaultMetaSchema(): void;
defaultMeta(): string | AnySchemaObject | undefined;
}
export = Ajv; Notice that there’s now an |
Any updates with version v8? |
This still doesn't work. This is also not working for me: import _Ajv from 'ajv';
const Ajv = _Ajv as unknown as typeof _Ajv.default;
const ajv = new Ajv(); I get an Only way I could make this work was with a import _Ajv from 'ajv';
// @ts-ignore
const Ajv = _Ajv.default;
const ajv = new Ajv(); |
This could easily be fixed by providing named exports for
The existing default export can be left untouched to avoid breaking any backwards compatibility. I'd be happy to make a PR for that. |
FWIW, my comment above is PR-able and will fully fix the types. I didn’t PR it because I noticed that no meaningful changes have been made to this repo in a while, and I don’t use this library personally. But anyone else is welcome to copy/paste and ping me for review. |
ajv-validator/ajv#2132 Signed-off-by: Jérôme Benoit <[email protected]>
@andrewbranch I'm willing/ready to open a PR, but the changes you describe I think only work if we can hand edit the .d.ts. In this case, the .d.ts is generated by TypeScript. I have so far been unsuccessful in writing a namespace in valid TypeScript that similarly re-exports the imports inside the namespace, like you suggested. I agree though that this does fix it, and I can verify the fixes work, when hand editing the .d.ts locally. |
It looks like this will do the trick class Ajv extends AjvCore {
+ static default = Ajv
// ...
}
- module.exports = exports = Ajv
+ export = Ajv
Object.defineProperty(exports, "__esModule", {value: true})
- export default Ajv
- export {
- Format,
- FormatDefinition,
- // ...
- } from "./types"
+ import * as types from "./types"
+ import * as core from "./core"
+ // ...
+ namespace Ajv {
+ export import Format = types.Format
+ export import FormatDefinition = types.FormatDefinition
+ // ...
+ } |
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 |
How about instead of the export = Ajv
Object.defineProperty(Ajv, "__esModule", {value: true})
+ Object.defineProperty(Ajv, "default", {value: Ajv})
+ declare namespace Ajv {
+ export { Ajv as default };
+ }
namespace Ajv {
export import Format = types.Format
} The issue is that the static property only confers a value meaning, not the type meaning of the |
That’s all eslint. |
Whooops yes it lol 🤦 |
Published |
Co-authored-by: Jason Ian Green <[email protected]>
This has been fixed in v8.13 via f4a4c8e: import { Ajv } from 'ajv' |
Thanks for the heads up @mosoriorian and @piotr-cz and anyone else involved. Closing as fixed in #2389 |
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:
package.json
and finally code:
I get the error as attached
Note that this code compiles correctly.
If I do:
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
The text was updated successfully, but these errors were encountered: