Typescript: TS4023: La variable exportée 'X' a ou utilise le nom 'Y' du module externe 'a / file / path' mais ne peut pas être nommée

Créé le 18 nov. 2015  ·  24Commentaires  ·  Source: microsoft/TypeScript

J'essaie de créer un ensemble de modules TypeScript externes comme suit:

// ClassA.ts
export default class ClassA {
  public method() { return true; } 
}

// ModuleB.ts
import ClassA from './ClassA';
var moduleB = {
  ClassA: ClassA
};
export default moduleB;

// ModuleA.ts
import moduleB from './ModuleB';
var moduleA = {
  moduleB: moduleB
}
export default moduleA;

L'idée est que cela sera compilé et fourni sous forme de bibliothèque. Le plan est que le consommateur de la bibliothèque instanciera une instance de ClassA comme suit:

import moduleA from './ModuleA';
var anInstance = moduleA.moduleB.ClassA();

Cela semble être en train de se compiler jusqu'au JS attendu, mais j'obtiens une erreur de compilation sur laquelle j'ai du mal à trouver plus d'informations.

Using tsc v1.6.2
.../src/ModuleA.ts(3,5): error TS4023: Exported variable 'moduleA' has or is using name 'ClassA' from external module ".../src/ClassA" but cannot be named.

Quelqu'un ici peut-il faire la lumière sur cette question? Ce que j'essaie de faire a-t-il un sens?

Question

Commentaire le plus utile

FWIW, vous obtiendrez généralement des réponses plus rapides sur Stack Overflow, mais nous remplissons ici des questions bien formulées.

Le problème ici est que vous utilisez l'indicateur --declaration , mais que vous n'avez pas fourni de moyen pour le compilateur de faire son travail.

Lorsqu'il essaie d'émettre ModuleA.d.ts , le compilateur doit écrire un littéral de type d'objet (par exemple { moduleB: { classA: *mumble?* } } ) représentant la forme du module. Mais il n'y a pas de nom dans la portée qui fait directement référence à classA , donc c'est le type "ne peut pas être nommé" et il y a une erreur.

Si vous ajoutez un import de ClassA à ModuleA.ts , l'erreur devrait disparaître.

Tous les 24 commentaires

Oh - si ce n'est pas un endroit approprié pour poser cette question. S'il vous plaît laissez-moi savoir et je serais heureux de le publier sur un autre support.

FWIW, vous obtiendrez généralement des réponses plus rapides sur Stack Overflow, mais nous remplissons ici des questions bien formulées.

Le problème ici est que vous utilisez l'indicateur --declaration , mais que vous n'avez pas fourni de moyen pour le compilateur de faire son travail.

Lorsqu'il essaie d'émettre ModuleA.d.ts , le compilateur doit écrire un littéral de type d'objet (par exemple { moduleB: { classA: *mumble?* } } ) représentant la forme du module. Mais il n'y a pas de nom dans la portée qui fait directement référence à classA , donc c'est le type "ne peut pas être nommé" et il y a une erreur.

Si vous ajoutez un import de ClassA à ModuleA.ts , l'erreur devrait disparaître.

Salut Ryan - merci pour les conseils de débordement de pile RE et les conseils - l'ajout de l'importation à ModuleA permis de compiler les choses comme prévu.

Un peu de contexte ici - mon objectif ici est en effet de générer des déclarations pour mes modules externes. (Quelque chose que je pense ne fonctionne pas encore tout à fait? # 5039?) Cette importation peut être complètement nécessaire mais cela semble un peu étrange pour ModuleA d'avoir besoin d'importer les symboles que moduleB exporte déjà. Le résultat est que chaque fois que j'ajoute à l'exportation d'objets de ModuleB , je devrai ajouter l'importation à ModuleA - soit directement:

import {moduleB} from './moduleB';
import {ClassA} from './ClassA';

Ou via ModuleB (dans le but de réduire la spécification de chemins pour ClassA aux deux endroits):

import {moduleB, ClassA} from './moduleB';

Je sais très peu de choses sur le compilateur TS - alors peut-être que ce que je dis est complètement irréaliste, mais au début, j'ai pensé que le compilateur pouvait en déduire que ModuleB exportait un objet avec N symboles dessus, donc ModuleA besoin de ces symboles? Les informations d'importation pour ModuleB sont déjà là - est-ce que ModuleA pourrait les utiliser? Ou serait-ce impossible parce que l'importation pour ClassA est complètement interne à ModuleB , il n'y a donc aucun moyen que le compilateur puisse déduire le type pour ClassA lors de la création de définitions pour ModuleA ?

Merci encore d'avoir pris le temps de répondre. J'ai réglé mon problème maintenant, donc ce qui précède est principalement de la curiosité - il n'y a pas de précipitation pour répondre.

Je ne sais pas exactement ce que vous dites. À quoi devrait ressembler ModuleA.d.ts dans cette proposition?

Le compilateur n'ajoutera pas de dépendances (c'est-à-dire des instructions d'importation) qui n'existaient pas dans le code utilisateur lorsqu'il émet des déclarations. l'erreur que vous obtenez signifie que le compilateur tente d'écrire une annotation de type pour une déclaration exportée mais n'a pas pu. cela peut avoir deux raisons, soit le nom n'est pas accessible, c'est-à-dire pas importé dans le module courant, soit il y a une déclaration qui masque la déclaration d'origine.

dans les deux cas, votre travail consiste à ajouter une annotation de type explicite, si vous ajoutez une annotation de type, elle sera émise textuellement dans la sortie; L'autre option est de vous assurer que le nom est accessible, c'est-à-dire que vous avez une importation dans le module, et vous comprenez ce que cela signifie pour vos utilisateurs qui importent votre module.

@mhegazy a dit:

Le compilateur n'ajoutera pas de dépendances (c'est-à-dire des instructions d'importation) qui n'existaient pas dans le code utilisateur lorsqu'il émet des déclarations.

Le problème est que je n'ai pas toujours besoin des instructions d'importation dans le code (évidemment, car cela fonctionne sans --declarations ), et les inclure est bruyant, ce qui fait que des choses comme tslint se plaignent du "importation non utilisée". Je peux voir pourquoi vous ne voudriez pas que le compilateur ajoute des dépendances au javascript émis, mais quel est le problème avec leur ajout aux déclarations d'émission?

n'hésitez pas à enregistrer un problème pour suivre cette suggestion. le rationnel était que les importations sont une déclaration de dépendance, et le compilateur ne devrait pas en créer une pour vous à moins que vous ne lui donniez l'ordre de le faire.

Cela pourrait avoir des conséquences plus profondes.

Considérez moduleA -> moduleB -> moduleC -> moduleD .

moduleB est celui qui doit faire import { ABC } from 'moduleA' pour contourner ce problème.

Quand moduleC utilise moduleB et exporte sa signature, la compilation échoue à nouveau car moduleB besoin de ABC de moduleA mais cette fois moduleB ne l'a pas exporté.

Cela signifie soit:

  1. moduleC doit avoir une dépendance dure de moduleA et importer ABC
  2. moduleB doit non seulement importer mais aussi réexporter ABC puis moduleC importe.

Si moduleD fait des choses similaires, alors vous devez connaître toute la chaîne de dépendances.

Je n'ai pas pu tester cela pour prouver que c'est le cas parce que j'utilise typings et qu'un problème me bloque donc: https://github.com/typings/typings/issues/625 ~~

EDIT: Je suis capable de le reproduire et en effet mon moduleD doit référencer moduleA pour faire l'importation.
Dans mon exemple:

  • moduleA => redux
  • moduleB => redux-thunk
  • moduleC => code personnalisé en plus de redux-thunk
  • moduleD => une bibliothèque
  • ABC => Dispatch interface en redux

J'ai le même problème décrit par @unional

Veuillez rouvrir ce problème, les problèmes décrits par @unional sont très réalistes et cela rend très difficile l'ajout de bibliothèques intermédiaires / auxiliaires au-dessus des bibliothèques tout en utilisant les mêmes types que le module d'origine que nous réexportons.

Émettez https://github.com/Microsoft/TypeScript/issues/9944 pistes ajoutant l'importation à la phase d'émission de déclaration.

Remercier!

Le problème avec l'ajout de l'importation est qu'avec noUnusedLocals le compilateur se plaindra du type non utilisé. Je pourrais ajouter une annotation de type explicite, mais je n'obtiens pas d'inférence. Exemple:

class Whatever {
  fetch(uri: string): Promise<void> { }
  ensureFetched = MemoizedFunction<(uri: string) => Promise<void>> = memoize((uri: string) => this.fetch(uri))
}

Je voudrais omettre l'annotation de type pour ensureFetched

J'ai trouvé une solution de contournement pour cela:
dans tsconfig: include: [ ..., "node_modules/@your_scope/your_library" ]
bonne chance et amusez-vous bien: smiley:

@ salim7 cela ralentit vos temps de compilation (car vous recompilez une bibliothèque qui aurait déjà dû être compilée) et vous oblige à utiliser le plus petit dénominateur commun des paramètres de rigueur avec la bibliothèque cible.

@mhegazy le problème s'est reproduit dans le scénario suivant:

import * as Foo from "./Foo";

export class Bar {
    baz = new Foo.Baz(); // Compiler forgot "Foo." prefix in the type, and throws this error, because "Baz" without perfix is not imported.
    getBaz() { // All the same
       return new Foo.Baz();
    }
}

La solution est de spécifier le type explicite:

import * as Foo from "./Foo";

export class Bar {
    baz: Foo.Baz = new Foo.Baz(); // ok
    getBaz(): Foo.Baz { // ok
       return new Foo.Baz();
    }
}

Je ne peux pas faire reproduire cela en utilisant l'exemple ci-dessus. veuillez déposer un nouveau bogue et fournir plus de contexte pour pouvoir reproduire le problème.

@PFight Merci! C'était tout pour moi!

Je viens de faire face à ce problème lors de l'utilisation:

export { IMyInterface } from './file'
````

The solution was to do this:
```ts
import { IMyInterface } from './file'
export { IMyInterface }

Mais cela ne devrait vraiment pas être nécessaire.

Quelqu'un a-t-il trouvé comment gérer noUnusedLocals ?

@yordis // @ts-ignore

@pelotom ouais c'est complètement ma faute,

Je pensais à une chose et j'ai écrit autre chose.

Typescript a-t-il résolu le problème entre noUnusedLocals et les déclarations?

Ma solution de contournement actuelle, qui me semble une douleur totale,

import {SomeInterface} from "./SomeFile";

const _dummySomeInterface : undefined|SomeInterface = undefined;
_dummySomeInterface;

//Code that implicitly uses SomeInterface

Évite noUnusedLocals , permet également l'inférence de type pour les interfaces génériques, si possible.

Cette page vous a été utile?
0 / 5 - 0 notes