typescript - Vad betyder "... resolves to a non-module entity and cannot be imported using this construct"?


Jag har några TypeScript-filer:

MyClass.ts

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

MyFunc.ts

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

MinKonsument.ts

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

Detta ger mig fel när jag försöker använda new

Modul "MyClass" löses till en icke-modulär enhet och kan inte importeras med denna konstruktion.

och när du försöker kalla fn()

Kan inte anropa ett uttryck vars typ saknar en anropssignatur.

Vad händer?

Svar #1

Varför det inte fungerar

import * as MC from './MyClass';

Detta är ES6/ES2015-stil import-syntax. Den exakta betydelsen av detta är "Ta modulens namespace-objekt som laddats från ./MyClass och använd det lokalt som MC". Noterbart är att "modulens namespace-objekt" endast består av ett vanligt objekt med egenskaper. Ett ES6-modulobjekt kan inte anropas som en funktion eller med new.

För att säga det igen: Ett ES6-modulnamnsobjekt kan inte anropas som en funktion eller med new.

Det som du importerar med * som X från ett modul är definierat att endast ha egenskaper. I nedgraderad CommonJS kanske detta inte respekteras fullt ut, men TypeScript talar om för dig vad beteendet som definieras av standarden är.

Vad fungerar?

Du kommer att behöva använda CommonJS-stilens importsyntax för att använda detta modul:

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

Om du kontrollerar båda modulerna kan du istället använda export default:

MyClass.ts

export default class MyClass {
  constructor() {
  }
}

MinKonsument.ts

import MC from './MyClass';

Jag är ledsen över detta; regler är dumma.

Det hade varit trevligt att använda ES6 importsyntax, men nu måste jag göra detta import MC=require('./MyClass');? Det är så 2013! Tråkig! Men sorg är en normal del av programmering. Fortsätt direkt till steg fem av Kübler-Ross-modellen: Acceptans.

TypeScript säger här till dig att detta inte fungerar, eftersom det inte fungerar. Det finns knep (att lägga till en namespace-deklaration till MyClass är ett populärt sätt att låtsas att det fungerar), och de kan fungera idag i just din nedgraderingsmodulpackare (t.ex. rollup), men detta är en illusion. Det finns ännu inga ES6-modulimplementationer i det vilda, men det kommer inte alltid att vara sant.

Föreställ dig ditt framtida jag, som försöker köra på en snygg inbyggd ES6-modulimplementation och upptäcker att du har ställt in dig för ett stort misslyckande genom att försöka använda ES6-syntax för att göra något som ES6 uttryckligen inte gör.

Jag vill utnyttja min icke-standard moduluppladdare

Kanske har du en modulladdare som "hjälpsamt" skapar default-exporter när inga finns. Jag menar, folk gör standarder av en anledning, men att ignorera standarder är ibland roligt och vi kan tycka att det är en cool sak att göra.

Ändra MyConsumer.ts till:

import A from './a';

Och specificera allowSyntheticDefaultImports-kommandoraden eller tsconfig.json-alternativet.

Observera att allowSyntheticDefaultImports inte ändrar ditt kods körbeteende alls. Det är bara en flagga som berättar för TypeScript att din modulhanterare skapar default-exporter när inga finns. Det kommer inte magiskt få din kod att fungera i nodejs om den inte gjorde det tidigare.

Svar #2

TypeScript 2.7 introducerar stöd genom att generera nya hjälpfunktioner: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#support-for-import-d-from-cjs-form-commonjs-modules-with---esmoduleinterop

Så i tsconfig.json lägger du till dessa två inställningar:

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

Och nu kan du använda:

import MyClass from './MyClass';

Relaterade artiklar