为什么在JavaScript中使用TypeScript类需要`.default`?

时间:2019-05-01 17:22:35

标签: javascript typescript

我有一个TypeScript类,它是npm包的一部分。为了维护,我将该类分解为多个类,并通过继承建立了最终的导出类。我认为这与我的问题无关,但我认为最好公开这些信息。我在ChildClass.ts中定义了这样的类:

export default ChildClass extends ParentClass{…}

Tsc的{build}为outDir
package.json文件具有属性"main": "build/ChildClass.js"

同时使用npm linknpm pack,我可以部署该包并在TypeScript演示包中毫无问题地使用它。但是,如果我尝试在JavaScript演示中使用该包,则

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

我得到了错误
 Cannot use 'new' with an expression whose type lacks a call or construct signature.ts(2351)

如果我通过添加new来更改.default语句,则:

const childClass = new ChildClass.default()

有效。我通过查看已编译的Javascript来解决这个问题。不得不使用.default使我感到震惊,这是我期望我的程序包的(假设的)JavaScript使用者知道的不合理的事情。我还发现,如果我避免使用export default而只使用export,则该程序包的工作更可预测,所以我就是这样做的。现在我可以使用

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

和类似的语法

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

在打字稿中。

仍然,我想知道这个神奇的.default属性是什么以及为什么需要它。

此外,我发现的所有其他有关此错误的参考文献似乎都与我所看到的无关。我认为记录此文档可能会帮助遇到类似错误的其他人。

1 个答案:

答案 0 :(得分:2)

背景:

ECMAScript语言规范specified中,导出的width: 100%;名称仅为* { box-sizing: border-box; position: relative; } .stepProgressBarContainer { width: 100%; } .stepProgressBarContainer .stepProgessBar { display: flex; flex-direction: row; justify-content: space-between; padding: 0px; } .stepProgressBarContainer .stepProgessBar li { list-style-type: none; position: relative; text-align: center; display: block; width: 100%; } .stepProgressBarContainer .stepProgessBar li:before { content: ''; display: block; width: 18px; height: 18px; -moz-border-radius: 9px; -webkit-border-radius: 9px; border-radius: 9px; background: #fff; border: 4px solid #ccc; box-sizing: border-box; text-align: center; margin: 0px auto 7.5px auto; } .stepProgressBarContainer .stepProgessBar li:after { content: ''; position: absolute; width: 100%; height: 4px; background-color: #ddd; top: 7px; left: -50%; z-index: -1; } .stepProgressBarContainer .stepProgessBar li:first-child:after { content: none; } .stepProgressBarContainer .stepProgessBar li:last-child:before { border-color: #2266e3; } .stepProgressBarContainer .stepProgessBar li.completed +:after { background: linear-gradient(0deg, #2266e3, #2266e3); } .stepProgressBarContainer .stepProgessBar li.completed:before { content: '\E73E'; border: 0; background: #2266e3; content: "\f007"; font-family: 'Font Awesome\ 5 Free'; font-weight: 900; }。因此,您可以将其视为名为<div class="stepProgressBarContainer"> <ul class="stepProgessBar"> <li class="completed">Step 1 2 3 4 5 5</li> <li>Step 2</li> <li>Step 3</li> <li>Step 4</li> <li>Step 5</li> </ul> </div>的普通导出。

将ES6模块编译为CommonJS仅具有一个事实标准,因为ECMAScript语言规范从未对此提及。目前,编译器(TypeScript和Babel)仅将所有导出内容放到export default,在这里默认的导出不再特殊。

仅当消费者也了解上述“标准”(例如TypeScript,Babel和Webpack)时,他们才会为您将"default"语句转换为"default"。当然,Node.js分别处理CommonJS和ES6模块,而不是这样做。

(实际上,这更加复杂,因为Babel将module.exportsimport ChildClass from 'my-package混合在一起,现代模块的消费者必须同时尝试这两者)


解决方案:

TypeScript具有special syntax const ChildClass = require('my-package').default,可以与传统的CommonJS使用者一起使用(它不在JavaScript中)。如果将import DefaultExport from 'some-package'更改为import * as AllExports from 'some-package,它应该可以按预期工作。