Que signifie «… se résout en une entité non-module et ne peut pas être importé à l'aide de cette construction»?

93

J'ai quelques fichiers 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();

Cela me donne des erreurs lorsque j'essaye d'utiliser new

Le module "MyClass" se résout en une entité non-module et ne peut pas être importé à l'aide de cette construction.

et en essayant d'appeler fn()

Impossible d'appeler une expression dont le type n'a pas de signature d'appel.

Ce qui donne?

Ryan Cavanaugh
la source
2
Merci d'avoir partagé une réponse. Je suggérerais de supprimer en javascripttant que balise principale et de laisser ecmascript-6, car la balise principale ici est typescript. La question suppose à tort que export =(une fonction TS) peut être associée à import ... from, alors qu'elle devrait l'êtreimport = . Il s'agit essentiellement d'import / export de module ES6 vs CJS / AMD.
Estus Flask

Réponses:

156

Pourquoi ça ne marche pas

import * as MC from './MyClass';

Il s'agit d'une importsyntaxe de style ES6 / ES2015 . La signification exacte de ceci est "Prenez l' objet d'espace de noms du module chargé ./MyClasset utilisez-le localement comme MC". Notamment, l '" objet d'espace de noms de module " consiste uniquement en un objet simple avec des propriétés. Un objet module ES6 ne peut pas être appelé en tant que fonction ou avec new.

Pour le répéter: un objet d'espace de noms de module ES6 ne peut pas être appelé en tant que fonction ou avec new.

La chose que vous importutilisez à * as Xpartir d'un module est définie pour n'avoir que des propriétés. Dans CommonJS de niveau inférieur, cela peut ne pas être entièrement respecté, mais TypeScript vous indique quel est le comportement défini par la norme.

Qu'est-ce qui fonctionne?

Vous devrez utiliser la syntaxe d'importation de style CommonJS pour utiliser ce module:

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

Si vous contrôlez les deux modules, vous pouvez utiliser export default place:

MyClass.ts

export default class MyClass {
  constructor() {
  }
}

MyConsumer.ts

import MC from './MyClass';

Je suis triste à ce sujet; Les règles sont stupides.

Cela aurait été bien d'utiliser la syntaxe d'importation ES6, mais maintenant je dois faire cette import MC = require('./MyClass');chose? C'est tellement 2013! Boiteux! Mais le deuil fait partie intégrante de la programmation. Veuillez passer à la cinquième étape du modèle Kübler-Ross: Acceptation.

TypeScript ici vous dit que cela ne fonctionne pas, car cela ne fonctionne pas. Il y a des hacks (ajouter une namespacedéclaration à MyClassest un moyen populaire de prétendre que cela fonctionne), et ils pourraient fonctionner aujourd'hui dans votre bundler de module de niveau inférieur (par exemple, rollup), mais c'est illusoire. Il n'y a pas encore d'implémentation de module ES6 dans la nature, mais ce ne sera pas toujours vrai.

Imaginez votre futur moi, en essayant de s'exécuter sur une implémentation de module ES6 native soignée et en constatant que vous vous êtes préparé à un échec majeur en essayant d'utiliser la syntaxe ES6 pour faire quelque chose que ES6 ne fait pas explicitement .

Je veux profiter de mon chargeur de module non standard

Peut-être avez-vous un chargeur de module qui crée «utilement» des defaultexportations lorsqu'il n'en existe pas. Je veux dire, les gens créent des normes pour une raison, mais ignorer les normes est parfois amusant et nous pouvons penser que c'est une bonne chose à faire.

Remplacez MyConsumer.ts par:

import A from './a';

Et spécifiez la allowSyntheticDefaultImportsligne de commande ou l' tsconfig.jsonoption.

Notez que allowSyntheticDefaultImportscela ne change pas du tout le comportement d'exécution de votre code. C'est juste un indicateur qui indique à TypeScript que votre chargeur de module crée des defaultexportations lorsqu'il n'en existe pas. Cela ne fera pas fonctionner comme par magie votre code dans nodejs alors que ce n'était pas le cas auparavant.

Ryan Cavanaugh
la source
Le style commonjs ne nécessite-t-il pas une cible commonjs? Existe-t-il un moyen de faire en sorte que cela fonctionne lorsque vous ciblez es6 / es2015?
Steve Buzonas
Vous ne pouvez pas le faire fonctionner en ciblant ES6 car cela ne fonctionne pas dans ES6 ...
Ryan Cavanaugh
Ai-je raison de comprendre que si je cible des modules ES2015, il n'y a aucun moyen de référencer un module CommonJS qui a export = MyClass? Ma seule option est de configurer mon module commonjset de continuer à rendre le monde pire en n'utilisant pas ES moderne?
Micah Zoltu
6
Dans les notes de version 2.7 , sous --esModuleInterop, il est dit "Nous recommandons fortement de l'appliquer à la fois aux projets nouveaux et existants". À mon avis, cette réponse (et l' entrée / politique de la FAQ dans DefinitelyTypedce lien ici) devrait être modifiée pour refléter la nouvelle position.
Alec Mev
1
Tellement très impertinent.
jmealy
25

TypeScript 2.7 introduit le support en émettant de nouvelles méthodes d'assistance: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#support-for-import-d-from-cjs-form- commonjs-modules-avec --- esmoduleinterop

Donc, dans tsconfig.json, ajoutez ces deux paramètres:

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

Et maintenant, vous pouvez utiliser:

import MyClass from './MyClass';
Michael
la source
Au lieu d'utiliser, esModuleInteropj'ai utilisé resolveJsonModuleavec votre autre suggestion d'utilisation allowSyntheticDefaultImportset cela a fonctionné pour moi.
Edgar Quintero le
6

Ajouter mes 2 cents ici au cas où quelqu'un d'autre aurait ce problème.

Ma façon de contourner le problème sans modifier tsconfig.json(ce qui peut être problématique dans certains projets), j'ai simplement désactivé la règle pour en ligne.

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

Shahar Hadas
la source
5

J'ai eu cette erreur en essayant d'inclure un package anti-rebond npm dans mon projet.

Lorsque j'ai essayé la solution acceptée ci-dessus, j'ai eu une exception:

L'attribution d'importation ne peut pas être utilisée lors du ciblage des modules ECMAScript. Pensez à utiliser 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', ou un autre format de module à la place.

Cela a fini par fonctionner:

import debounce from 'debounce' 
NSjonas
la source