TypeScript esModuleInterop应该在编译为esnext模块时也能正常工作,

wgx48brx  于 5个月前  发布在  TypeScript
关注(0)|答案(9)|浏览(82)

TypeScript 版本: 2.7.2
搜索词: esModuleInterop, esnext, modules, import, export, default
代码

使用这个类型定义:

declare function fn(): void;
declare module "external" {
  export = fn;
}

运行时:

tsc --esModuleInterop --module esnext

导入时产生以下错误:

import fn1 from 'external';       // error TS1192: Module '"external"' has no default export.
import fn2 = require('external'); // error TS1202: Import assignment cannot be used when targeting ECMAScript modules.

但是,如果你使用 CommonJS 模块:

tsc --esModuleInterop --module commonjs

它按预期工作(因为 --esModuleInterop )

import fn1 from 'external';       // works
import fn2 = require('external'); // works

预期行为:

可以理解的是,当它不在 helpers 中编译时,类型检查器不想假装导入是 interop'd。
但是,如果你已经指定了 --esModuleInterop--module esnext ,那么类型检查器的假设应该是外部系统正在应用 interop。否则,你为什么要指定 --esModuleInterop 呢?

Playground 链接:https://github.com/jamiebuilds/ts-bug

flvlnr44

flvlnr441#

这个问题实际上是关于到底要发出什么内容。当你想要将内容发出到ES模块时,一个默认的导入确实需要继续保持为默认导入。因此,esModuleInterop既涉及到类型检查,也涉及到默认值。
另一方面,当你期望外部工具(例如Babel、Webpack、SystemJS)自行拼接ES互操作性时,这时你可以使用allowSyntheticDefaultImports
我想你可以提出这样的观点:esModuleInterop应该暗示allowSyntheticDefaultImports并只发出空内容,但@weswigham可能有更好的见解。

mfpqipee

mfpqipee2#

--allowSyntheticDefaultImports的问题在于它仍然将import * as foo from 'cjs'视为与import foo from 'cjs'相同。理想情况下的行为应该是类似于"--requireSyntheticDefaultImports"。

nkkqxpd9

nkkqxpd93#

我猜你可以提出这样的观点:esModuleInterop应该暗示允许合成默认导入
@DanielRosenwasser 它不是已经实现了吗?来自 --esModuleInterop docs

  • 为运行时babel生态系统兼容性生成__importStar和__importDefault辅助函数,并启用--allowSyntheticDefaultImports以实现类型系统的兼容性。*
jecbmhm3

jecbmhm34#

allowSyntheticDefaultImports 无法解决这个问题。当你重新导出使用它导入的内容时,它还具有破坏性行为:你得到一个形状为 { default: T } 的对象,而不是 T

daupos2t

daupos2t5#

请注意,Node 12.x 仅在从 ESModules 导入 CommonJS 模块时支持 default 导入:

import legacy from "cjs-package";

这不起作用:

import { method } from "cjs-package";

唯一例外的是内置模块,它们有特殊的处理方式。

carvr3hs

carvr3hs6#

这个问题似乎在TypeScript 3.2、3.1或更高版本中已经解决了。

bvjxkvbb

bvjxkvbb7#

你好,请问这个问题已经解决了吗?我遇到了一个类似的问题,就像@jamiebuildshere提到的那样。

我正在使用一个库,它导出一个名为 default 的函数。当我导入这个对象时,根据我的 TypeScript 项目是设置为 esm 还是 commonjs,我会得到两个不同的对象表示。

项目沙箱可以在 here 找到。

TypeScript 项目设置为 commonjs

  • "type": "commonjs"package.json
  • "module": "commonjs"tsconfig.json
  • 运行中 node -r ts-node/register src/index.ts
  • 我得到的是 [Function: standaloneCode],这是预期的输出

TypeScript 项目设置为 esm

  • "type": "module"package.json
  • "module": "esnext"tsconfig.json
  • 运行中 node --loader ts-node/esm src/index.ts
  • 我得到的是 { default: [Function: standaloneCode] }。这弄乱了我的类型定义。

我已经尝试过使用 "esModuleInterop": true"allowSyntheticDefaultImports": true,但似乎没有太大的区别。
如果有任何解决方法或建议,我将非常感激。

6uxekuva

6uxekuva8#

你的这个问题与这个issue不同。这是当前TypeScript规范的问题。
ajv的main具有a special hack,使其从TS commonjs和esm中都可读,
https://github.com/ajv-validator/ajv/blob/8ffe5faca9c4b49fc538b35399a1b4febba0bc41/lib/ajv.ts#L34-L35
但是你的ajv/dist/standalone/index.js没有这样的技巧。
https://github.com/ajv-validator/ajv/blob/8ffe5faca9c4b49fc538b35399a1b4febba0bc41/lib/standalone/index.ts

mcdcgff0

mcdcgff09#

感谢@teppeis。明白了。我会在ajv仓库中提出这个问题。

相关问题