O que significa “... resolve para uma entidade não módulo e não pode ser importado usando esta construção”?

93

Tenho alguns arquivos TypeScript:

MyClass.ts

class MyClass {
  constructor() {
  }
}
export = MyClass;

MyFunc.ts

function fn() { return 0; }
export = fn;

MyConsumer.ts

import * as MC from './MyClass';
import * as fn from './MyFunc';
fn();

Isso me dá erros ao tentar usar new

O módulo "MyClass" é resolvido como uma entidade não módulo e não pode ser importado usando esta construção.

e ao tentar ligar fn()

Não é possível invocar uma expressão cujo tipo não tem assinatura de chamada.

O que da?

Ryan Cavanaugh
fonte
2
Obrigado por compartilhar uma resposta. Eu sugeriria remover javascriptcomo uma tag primária e sair ecmascript-6, porque a tag primária aqui é typescript. A questão assume erroneamente que export =(um recurso TS) pode ser emparelhado com import ... from, embora deva ser emparelhado comimport = . É basicamente importação / exportação de módulo ES6 vs CJS / AMD.
Estus Flask,

Respostas:

156

Por que não funciona

import * as MC from './MyClass';

Esta é a importsintaxe do estilo ES6 / ES2015 . O significado exato disso é "Pegue o objeto de namespace do módulo carregado de ./MyClasse use-o localmente como MC". Notavelmente, o " objeto de namespace do módulo " consiste apenas em um objeto simples com propriedades. Um objeto de módulo ES6 não pode ser chamado como uma função ou comnew .

Repetindo : um objeto de namespace do módulo ES6 não pode ser invocado como uma função ou comnew .

A coisa que você está importusando* as X em um módulo é definido para ter apenas propriedades. Em CommonJS de nível inferior, isso pode não ser totalmente respeitado, mas o TypeScript está dizendo a você qual é o comportamento definido pelo padrão.

O que funciona?

Você precisará usar a sintaxe de importação do estilo CommonJS para usar este módulo:

import MC = require('./MyClass');

Se você controlar os dois módulos, você pode usar export default :

MyClass.ts

export default class MyClass {
  constructor() {
  }
}

MyConsumer.ts

import MC from './MyClass';

Estou triste com isso; As regras são mudas.

Teria sido bom usar a sintaxe de importação ES6, mas agora eu tenho que fazer isso import MC = require('./MyClass');? É tão 2013! Lame! Mas o luto é uma parte normal da programação. Por favor, pule para o estágio cinco no modelo Kübler-Ross: Aceitação.

O TypeScript aqui está dizendo que isso não funciona, porque não funciona. Existem hacks (adicionar uma namespacedeclaração a MyClassé uma forma popular de fingir que isso funciona) e eles podem funcionar hoje em seu bundler de módulo de downlevel específico (por exemplo, rollup), mas isso é ilusório. Ainda não existem implementações de módulo ES6 disponíveis, mas isso não será verdade para sempre.

Imagine seu futuro, tentando executar em uma implementação de módulo ES6 neato nativo e descobrindo que você se preparou para uma falha grave ao tentar usar a sintaxe ES6 para fazer algo que o ES6 explicitamente não faz .

Quero aproveitar as vantagens do meu carregador de módulo não padrão

Talvez você tenha um carregador de módulo que "utilmente" cria defaultexportações quando não existem. Quer dizer, as pessoas fazem padrões por uma razão, mas ignorar padrões é divertido às vezes e podemos achar que é uma coisa legal de se fazer.

Altere MyConsumer.ts para:

import A from './a';

E especifique a allowSyntheticDefaultImportslinha de comando ou tsconfig.jsonopção.

Observe que allowSyntheticDefaultImportsnão muda o comportamento do tempo de execução do seu código. É apenas um sinalizador que informa ao TypeScript que o carregador de módulo cria defaultexportações quando não existem. Ele não fará seu código funcionar magicamente em nodejs quando não funcionava antes.

Ryan Cavanaugh
fonte
O estilo commonjs não requer um alvo commonjs? Existe uma maneira de fazer isso funcionar quando você está direcionando o ES6 / ES2015?
Steve Buzonas
Você não pode fazer funcionar visando ES6 porque não funciona em ES6 ...
Ryan Cavanaugh
Estou correto em entender que, se estou visando módulos ES2015, não há como fazer referência a um módulo CommonJS que tenha export = MyClass? Minha única opção é definir meu módulo como commonjse continuar a tornar o mundo um lugar pior por não usar o ES moderno?
Micah Zoltu
6
Nas notas de versão 2.7 , em --esModuleInterop, diz "É altamente recomendável aplicá-lo a projetos novos e existentes". Em minha opinião, esta resposta (e a política / entrada do FAQ nos DefinitelyTypedlinks aqui) deve ser modificada para refletir a nova postura.
Alec Mev
1
Tão atrevido.
jmealy
25

TypeScript 2.7 apresenta suporte emitindo novos métodos auxiliares: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#support-for-import-d-from-cjs-form- commonjs-modules-with --- esmoduleinterop

Portanto, em tsconfig.json, adicione estas duas configurações:

{
    // Enable support for importing CommonJS modules targeting es6 modules
    "esModuleInterop": true,

    // When using above interop will get missing default export error from type check since
    // modules use "export =" instead of "export default", enable this to ignore errors.
    "allowSyntheticDefaultImports": true
}

E agora você pode usar:

import MyClass from './MyClass';
Michael
fonte
Em vez de usar esModuleInterop, usei resolveJsonModulejunto com sua outra sugestão de uso allowSyntheticDefaultImportse funcionou para mim.
Edgar Quintero
6

Adicionando meus 2 centavos aqui caso outra pessoa tenha esse problema.

Minha maneira de contornar o problema sem modificar tsconfig.json(o que pode ser problemático em alguns projetos), simplesmente desativei a regra para oneline.

import MC = require('./MyClass'); // tslint:disable-line

Shahar Hadas
fonte
5

Recebi este erro ao tentar incluir um pacote de debounce npm em meu projeto.

Quando tentei a solução aceita acima, obtive uma exceção:

A atribuição de importação não pode ser usada ao direcionar módulos ECMAScript. Considere usar 'import * as ns de "mod"', 'import {a} de "mod"', 'import d de "mod"' ou outro formato de módulo.

Isso acabou funcionando:

import debounce from 'debounce' 
NSjonas
fonte