Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does using TypeScript class in JavaScript require `.default`?

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 compiled 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.

like image 283
Tod Avatar asked May 01 '19 17:05

Tod


People also ask

What is default in TypeScript?

The default parameter in TypeScript is used to assign some default value to the variable. In JavaScript, we have this provision not to pass the required argument to the called function because it internally manages, But in TypeScript, we cannot do this.

Should I use default exports?

I prefer to use default exports when the exported component is only going to be imported once, or if a file exports one thing. A good example would be a Router component.

What does export default do in JavaScript?

Export Default is used to export only one value from a file which can be a class, function, or object. The default export can be imported with any name.

How do you use a class in TypeScript?

You can do this by using the new keyword, followed by a syntax similar to that of an arrow function, where the parameter list contains the parameters expected by the constructor and the return type is the class instance this constructor returns. The TypeScript compiler now will correctly compile your code.


1 Answers

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 becomes 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 complicated 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.

like image 132
yume_chan Avatar answered Sep 21 '22 17:09

yume_chan