表題のエラーに遭遇したので、その原因と対処法を記録しておきます。
環境
この記事は以下の環境を前提にしています。
▼package.json
"devDependencies"{
"@babel/cli": "^7.10.5",
"@babel/core": "^7.10.5",
"@babel/preset-env": "^7.10.4",
"typescript": "^3.8.3"
}
記事を読む前に、お手元の環境をご確認ください。
問題発生の手順
- npmでES6ネイティブのモジュールをインストールする。
- モジュール内のクラスを継承する。
- BabelなどでES5にトランスパイルする。
import { ES6Class } from "module";
export class ClassName extends ES6Class {...
上記コードの例の場合、npmからES6ネイティブのmodule
という名前のパッケージをインストールし、
ES6Class
というクラスをインポート、ClassName
というクラスに継承しています。
症状
上記の手順を実行し、生成されたjavascriptを実行すると、以下のエラーが発生します。
Uncaught TypeError: Class constructor ClassName cannot be invoked without 'new'
at new ClassName (ClassName.js:35)
at onDomContentsLoaded (main.js:19)
at eval (main.js:32)
at Module../src/main.js (bundle.js:133)
at __webpack_require__ (bundle.js:20)
at bundle.js:84
at bundle.js:87
classname @ classname.js:35
onDomContentsLoaded @ main.js:19
(anonymous) @ main.js:32
./src/main.js @ bundle.js:133
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:84
(anonymous) @ bundle.js:87
ClassName
クラスのコンストラクターの実行に失敗します。
原因
この問題は、ネイティブのES6クラスを継承し、さらにES5に変換することによって発生します。この変換に失敗するため、上記の症状が発生します。
対処法
あなたが問題の発生するパッケージの利用者か、開発者かによって対処法が分かれます。
あなたがパッケージの利用者の場合
変換の対象をES5ではなくES6に指定することで、この問題は解決します。
Babelの場合
Babelでトランスパイルを使用している場合、@babel/preset-env
で変換対象を指定します。
node
を変換対象に指定することでES6のjsファイルが出力されます。
▼.babelrc
{
"presets": [
[ "@babel/preset-env", {
"targets": { "node": true }
}]
]
}
TypeScriptの場合
TypeScriptの場合、コンパイラオブションで変換対象の指定ができます。
▼tsconfig.json
{
"compilerOptions": {
"target": "es6",
}
}
副作用
変換先のjsファイルはES6の構文を使用していますので、IE11では動作しません。
ECMAScript 6 compatibility table
IE11をターゲットとする場合は、extendsを使用しないなど他の回避方法を検討してください。
あなたがパッケージの開発者の場合
問題が発生するパッケージを、CommonJS
とESModules
の両対応にすることで問題が解決します。npmモジュールに両方のスクリプトがあり、その状態がpackage.jsonに記述されていれば、webpackなどのモジュールバンドラーがファイルを適切に取りあつかってくれます。
TypeScriptの場合
まずはCommonJSを出力するtsconfig.json
を用意します。この設定では、仮にlib
というディレクトリにCommonJSと型定義ファイルが出力されます。
▼tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"declaration": true,
"declarationMap": true,
"outDir": "./lib"
}
}
tsconfig.json
を継承するtsconfig.esm.json
を用意します。esm
というディレクトリに、ESModulesが出力されます。こちらでは型定義ファイルは出力しません。
▼tsconfig.esm.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"target": "es6",
"module": "es2015",
"outDir": "./esm",
"declaration": false,
"declarationMap": false
}
}
最後に、package.jsonにこの2つのファイルの場所を記述します。
▼package.json
{
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"module": "./esm/index.js",
}
package.jsonにmodule
フィールドを追加し、ESModules形式で出力されたファイルを指定します。モジュールバンドラーはこのフィールドを参照して、必要なファイルを取り出します。
参考記事
stackoverflow - Javascript ES6 TypeError: Class constructor Client cannot be invoked without 'new'