admin管理员组

文章数量:1287621

I have a TypeScript class that's part of an npm package. For maintenance, I broke up the class into multiple classes, and I built up the ultimate exported class through inheritance. I don't think this matters to my question, but I figure it's better to disclose this bit of information. I defined the class in ChildClass.ts like this:

export default ChildClass extends ParentClass{…}

Tsc has an outDir of "build."
The package.json file has a property "main": "build/ChildClass.js"

Using both npm link and npm pack and I can deploy the package and consume it with no problem in a TypeScript demo package. However, if I try to use the package in a JavaScript demo,

const ChildClass = require('my-package')
const childClass = new ChildClass()

I get the error
Cannot use 'new' with an expression whose type lacks a call or construct signature.ts(2351)

If I change the new statement by adding .default like so:

const childClass = new ChildClass.default()

It works. I figured this out by looking at the piled Javascript. Having to use .default struck me as an unreasonable thing to expect (the hypothetical) JavaScript consumers of my package to know. I also discovered that if I avoided export default and just used export then the package works more predictably, so that's what I did. Now I can use

const {ChildClass} = require('my-package')
const childClass = new ChildClass()

and the similar syntax

import {ChildClass} from 'my-package'
const childClass = new ChildClass()

in typescript.

Still, I would like to know what this magic .default property is and why I need it.

Also, all other references I found to this error didn't seem relevant to what I was seeing; I thought documenting this might help someone else that runs across a similar error.

I have a TypeScript class that's part of an npm package. For maintenance, I broke up the class into multiple classes, and I built up the ultimate exported class through inheritance. I don't think this matters to my question, but I figure it's better to disclose this bit of information. I defined the class in ChildClass.ts like this:

export default ChildClass extends ParentClass{…}

Tsc has an outDir of "build."
The package.json file has a property "main": "build/ChildClass.js"

Using both npm link and npm pack and I can deploy the package and consume it with no problem in a TypeScript demo package. However, if I try to use the package in a JavaScript demo,

const ChildClass = require('my-package')
const childClass = new ChildClass()

I get the error
Cannot use 'new' with an expression whose type lacks a call or construct signature.ts(2351)

If I change the new statement by adding .default like so:

const childClass = new ChildClass.default()

It works. I figured this out by looking at the piled Javascript. Having to use .default struck me as an unreasonable thing to expect (the hypothetical) JavaScript consumers of my package to know. I also discovered that if I avoided export default and just used export then the package works more predictably, so that's what I did. Now I can use

const {ChildClass} = require('my-package')
const childClass = new ChildClass()

and the similar syntax

import {ChildClass} from 'my-package'
const childClass = new ChildClass()

in typescript.

Still, I would like to know what this magic .default property is and why I need it.

Also, all other references I found to this error didn't seem relevant to what I was seeing; I thought documenting this might help someone else that runs across a similar error.

Share Improve this question asked May 1, 2019 at 17:22 TodTod 8,2526 gold badges55 silver badges95 bronze badges 1
  • 6 Because CommonJS (Node's require() and exports) is not patible with ES6 default exports. The solution to down-pile but still allow ES6 spec pliant module loaders to work is to emit __esModule on the exported value. Babel does the same thing. IMO just avoid default exports, they aren't very useful and create problems. – Aaron Beall Commented May 1, 2019 at 17:38
Add a ment  | 

1 Answer 1

Reset to default 13

Background:

The ECMAScript Language Specification specified that the exported name of export default is just "default". So you can think about it as an ordinary export named "default".

Compiling a ES6 module to CommonJS only has a de facto standard, as the ECMAScript Language Specification never mentioned about it. Currently transpilers (TypeScript and Babel) just put all exports to module.exports, where default export bees not special anymore.

Only when the consumers also understand the above "standard" (e.g. TypeScript, Babel and Webpack), they will transform import ChildClass from 'my-package statements to const ChildClass = require('my-package').default for you. Of course, Node.js deals with CommonJS and ES6 modules separately, and not doing this.

(Actually it's more plicated because Babel mixed import DefaultExport from 'some-package' and import * as AllExports from 'some-package, modern module consumers has to try both)


Solutions:

TypeScript has a special syntax export = to work with tranditional CommonJS consumers (It's not in JavaScript). If you change your export default class ChildClass... to export = class ChildClass..., it should work as you expected.

本文标签: Why does using TypeScript class in JavaScript require defaultStack Overflow