javascript - Standard klasseinstansiering resulterer i TypeError (ESM/CJS interoperabilitet)
Sagsoversigt
Hej,
Jeg har et TypeScript-projekt, hvor jeg forsøger at instantiere en klasse, som var standardeksporten fra en anden pakke. Jeg skriver mit projekt i ESM-syntaks, mens pakken, det afhænger af, har CJS-output. Problemet, jeg støder på, er, at når kørslen når punktet for klasseinstansiering, får jeg følgende fejl -
new TestClass({ arg1: "Hello, World!" });
^
TypeError: TestClass is not a constructor
Kode
//My package.json
{
"name": "myproject",
"version": "1.0.0",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"type": "module",
"dependencies": {
"testpackage": "^1.0.0",
"typescript": "^5.0.4"
},
"devDependencies": {
"@types/node": "^20.2.5"
}
}
//My index.ts
import TestClass from "testpackage";
new TestClass({ arg1: "Hello, World!" });
//My tsconfig.json
{
"include": ["src"],
"compilerOptions": {
"outDir": "dist",
"lib": ["es2023"],
"target": "es2022",
"moduleResolution": "node"
}
}
//Dependency's package.json
{
"name": "testpackage",
"version": "1.0.0",
"description": "TestPackage",
"main": "./dist/testFile.js",
"exports": "./dist/testFile.js",
"scripts": {
"build": "tsc"
},
"files": ["dist"],
"devDependencies": {
"@types/node": "^20.2.5",
"typescript": "^5.0.4"
}
}
//Dependency's testFile.ts
export default class TestClass {
constructor({ arg1 }: { arg1: string }) {
console.log(arg1);
}
}
//Dependency's tsconfig.json
{
"include": ["src"],
"compilerOptions": {
"declaration": true,
"lib": ["es2023"],
"target": "es6",
"module": "CommonJS",
"outDir": "dist"
}
}
//Dependency's testFile.js output
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class TestClass {
constructor({ arg1 }) {
console.log(arg1);
}
}
exports.default = TestClass;
Ting fungerer fint, hvis jeg fjerner "type": "module" fra min package.json. De fungerer også fint, hvis klassen er et navngivet eksport i stedet for et standardeksport i afhængighedens kode. Er dette en kendt inkompatibilitet, når man prøver at importere CJS til ESM, eller gør jeg noget forkert her?
Bemærk - Hvis jeg sætter "moduleResolution": "nodenext" i min tsconfig.json, bliver fejlen genereret allerede ved kompilering
src/index.ts:3:5 - error TS2351: This expression is not constructable.
Type 'typeof import("<project_dir>/node_modules/testpackage/dist/testFile")' has no construct signatures.
3 new TestClass({ arg1: "Hello, World!" });
~~~~~~~~~
Found 1 error in src/index.ts:3
Svar #1
Der er kendte kompatibilitetsproblemer mellem CommonJS (CJS) og ECMAScript-moduler (ESM). I ESM bliver standardeksporterne fra CJS-moduler pakket ind i standardegenskaber i stedet for at blive eksponeret direkte. På den anden side påvirkes navngivne eksporter ikke og kan importeres direkte.
Hvis du angiver "type": At angive "module" i package.json får Node.js til at behandle .js-filen som ESM. Derfor skal du importere modulet ved hjælp af ESM-importerklæringen. Hvis modulet, du forsøger at importere, dog er i CJS-format, vil du støde på kompatibilitetsproblemer.
Der er flere muligheder for at rette dette.
- Få adgang til klassen gennem standardegenskaben som beskrevet ovenfor.
import Test from 'package-name';
const TestClass = Test.default;
For at undgå problemer forårsaget af at blande de to modulformater, skal al kode omdannes til enten ESM eller CJS.
Indlæs CJS-modulet ved hjælp af Node.js-funktionen createRequire.
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const TestClass = require('package-name');
