¿Qué significa "... se resuelve en una entidad que no es un módulo y no se puede importar con esta construcción"?

93

Tengo algunos archivos de 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();

Esto me da errores al intentar usar new

El módulo "MyClass" se resuelve como una entidad que no es un módulo y no se puede importar con esta construcción.

y al intentar llamar fn()

No se puede invocar una expresión cuyo tipo carece de una firma de llamada.

¿Lo que da?

Ryan Cavanaugh
fuente
2
Gracias por compartir una respuesta. Sugeriría eliminar javascriptcomo etiqueta principal y salir ecmascript-6, porque la etiqueta principal aquí es typescript. La pregunta asume erróneamente que export =(una función de TS) se puede emparejar con import ... from, mientras que debería emparejarse conimport = . Es básicamente la importación / exportación del módulo ES6 frente a CJS / AMD.
Estus Flask

Respuestas:

156

Por que no funciona

import * as MC from './MyClass';

Esta es la importsintaxis de estilo ES6 / ES2015 . El significado exacto de esto es "Tomar el objeto del espacio de nombres del módulo cargado ./MyClassy usarlo localmente como MC". En particular, el " objeto de espacio de nombres de módulo " consta solo de un objeto simple con propiedades. Un objeto de módulo ES6 no se puede invocar como función o con new.

Para decirlo de nuevo: un objeto de espacio de nombres de módulo ES6 no se puede invocar como una función o con new.

Lo que importusa * as Xde un módulo está definido para que solo tenga propiedades. En CommonJS rebajado, esto podría no respetarse por completo, pero TypeScript le dice cuál es el comportamiento definido por el estándar.

¿Qué funciona?

Necesitará usar la sintaxis de importación estilo CommonJS para usar este módulo:

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

Si controlas ambos módulos, puedes usar export defaulten su lugar:

MyClass.ts

export default class MyClass {
  constructor() {
  }
}

MyConsumer.ts

import MC from './MyClass';

Estoy triste por esto; Las reglas son tontas.

Hubiera sido bueno usar la sintaxis de importación de ES6, pero ahora tengo que hacer esto import MC = require('./MyClass');. ¡Es tan 2013! ¡Cojo! Pero el dolor es una parte normal de la programación. Salte a la etapa cinco en el modelo Kübler-Ross: Aceptación.

TypeScript aquí le dice que esto no funciona, porque no funciona. Hay hacks (agregar una namespacedeclaración a MyClasses una forma popular de pretender que esto funciona), y podrían funcionar hoy en su paquete de módulos de bajada de nivel particular (por ejemplo, rollup), pero esto es ilusorio. Todavía no hay implementaciones del módulo ES6 en la naturaleza, pero eso no será cierto para siempre.

Imagínese usted mismo en el futuro, tratando de ejecutar una implementación nativa del módulo ES6 y descubriendo que se ha preparado para una falla importante al intentar usar la sintaxis de ES6 para hacer algo que ES6 explícitamente no hace .

Quiero aprovechar mi cargador de módulos no estándar

Tal vez tenga un cargador de módulos que "provechosamente" crea defaultexportaciones cuando no existen. Quiero decir, la gente crea estándares por una razón, pero ignorarlos a veces es divertido y podemos pensar que es algo genial.

Cambie MyConsumer.ts por:

import A from './a';

Y especifique la allowSyntheticDefaultImportslínea de comandos u tsconfig.jsonopción.

Tenga en cuenta que allowSyntheticDefaultImportsno cambia el comportamiento en tiempo de ejecución de su código en absoluto. Es solo una bandera que le dice a TypeScript que su cargador de módulos crea defaultexportaciones cuando no existen. No hará que su código funcione mágicamente en nodejs cuando antes no lo hacía.

Ryan Cavanaugh
fuente
¿No requiere el estilo commonjs un objetivo commonjs? ¿Hay alguna manera de hacer que esto funcione cuando se dirige a es6 / es2015?
Steve Buzonas
No puede hacer que funcione apuntando a ES6 porque no funciona en ES6 ...
Ryan Cavanaugh
¿Estoy en lo cierto al entender que si me dirijo a módulos ES2015, no hay forma de hacer referencia a un módulo CommonJS que tenga export = MyClass? ¿Mi única opción es configurar mi módulo commonjsy continuar haciendo del mundo un lugar peor al no usar ES moderno?
Micah Zoltu
6
En las notas de la versión 2.7 , debajo --esModuleInterop, dice "Recomendamos encarecidamente aplicarlo a proyectos nuevos y existentes". En mi opinión, esta respuesta (y la entrada / política de preguntas frecuentes en DefinitelyTypedesos enlaces aquí) deberían modificarse para reflejar la nueva postura.
Alec Mev
1
Muy atrevido.
jmealy
25

TypeScript 2.7 introduce soporte mediante la emisión de nuevos métodos de ayuda: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#support-for-import-d-from-cjs-form- commonjs-modules-con --- esmoduleinterop

Entonces en tsconfig.json agregue estas dos configuraciones:

{
    // 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
}

Y ahora puedes usar:

import MyClass from './MyClass';
Miguel
fuente
En lugar de usar esModuleInterop, usé resolveJsonModulejunto con su otra sugerencia de uso allowSyntheticDefaultImportsy funcionó para mí.
Edgar Quintero
6

Agregando mis 2 centavos aquí en caso de que alguien más tenga este problema.

En mi forma de solucionar el problema sin modificar tsconfig.json(lo que puede ser problemático en algunos proyectos), he optado por deshabilitar simplemente la regla para la línea.

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

Shahar Hadas
fuente
5

Recibí este error al intentar incluir un paquete antirrebote npm en mi proyecto.

Cuando probé la solución aceptada anterior, obtuve una excepción:

La asignación de importación no se puede utilizar al seleccionar módulos ECMAScript. Considere usar 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', u otro formato de módulo en su lugar.

Esto terminó funcionando:

import debounce from 'debounce' 
NSjonas
fuente