Typescript: TS4023: A variável exportada 'X' tem ou está usando o nome 'Y' do módulo externo 'um / arquivo / caminho', mas não pode ser nomeada

Criado em 18 nov. 2015  ·  24Comentários  ·  Fonte: microsoft/TypeScript

Estou tentando criar um conjunto de módulos TypeScript externos da seguinte maneira:

// 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;

A ideia é que isso seja compilado e fornecido como uma biblioteca. O plano é que o consumidor da biblioteca instancie uma instância de ClassA da seguinte forma:

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

Parece que está compilando para o JS esperado, mas estou recebendo um erro do compilador, sobre o qual estou lutando para encontrar mais informações.

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.

Alguém aqui pode lançar alguma luz sobre este assunto? O que estou tentando fazer faz sentido?

Question

Comentários muito úteis

Normalmente, você obterá respostas mais rápidas no Stack Overflow, mas respondemos a perguntas bem formuladas aqui.

O problema aqui é que você está usando o sinalizador --declaration , mas não forneceu uma maneira para o compilador fazer seu trabalho.

Ao tentar emitir ModuleA.d.ts , o compilador precisa escrever um literal de tipo de objeto (por exemplo, { moduleB: { classA: *mumble?* } } ) representando a forma do módulo. Mas não há um nome no escopo que se refira diretamente a classA , então é o tipo "não pode ser nomeado" e há um erro.

Se você adicionar import de ClassA a ModuleA.ts , o erro desaparecerá.

Todos 24 comentários

Oh - se este não for um lugar adequado para fazer esta pergunta. Informe-nos e terei todo o gosto em publicá-lo noutro meio.

Normalmente, você obterá respostas mais rápidas no Stack Overflow, mas respondemos a perguntas bem formuladas aqui.

O problema aqui é que você está usando o sinalizador --declaration , mas não forneceu uma maneira para o compilador fazer seu trabalho.

Ao tentar emitir ModuleA.d.ts , o compilador precisa escrever um literal de tipo de objeto (por exemplo, { moduleB: { classA: *mumble?* } } ) representando a forma do módulo. Mas não há um nome no escopo que se refira diretamente a classA , então é o tipo "não pode ser nomeado" e há um erro.

Se você adicionar import de ClassA a ModuleA.ts , o erro desaparecerá.

Olá Ryan - obrigado pelo conselho sobre estouro de pilha de RE e o conselho - adicionar a importação a ModuleA fez com que as coisas fossem compiladas conforme o esperado.

Um pouco de contexto aqui - meu objetivo aqui é realmente gerar declarações para meus módulos externos. (Algo que eu acredito que ainda não está funcionando? # 5039?) Esta importação pode ser completamente necessária, mas parece um pouco estranho para ModuleA precisar importar os símbolos que o móduloB já exporta. O resultado é que toda vez que eu adicionar a exportação de objeto de ModuleB , precisarei adicionar a importação a ModuleA - diretamente:

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

Ou via ModuleB (em um esforço para reduzir a especificação de caminhos para ClassA em ambos os lugares):

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

Eu sei muito pouco sobre o compilador TS - então talvez o que estou dizendo seja completamente irreal, mas a princípio parecia que o compilador poderia deduzir que ModuleB está exportando um objeto com N símbolos, então ModuleA precisa desses símbolos? As informações de importação de ModuleB já estão lá - ModuleA poderia usá-las? Ou isso seria impossível porque a importação de ClassA é completamente interna a ModuleB , então não há como o compilador deduzir o tipo de ClassA ao criar definições de ModuleA ?

Obrigado mais uma vez por responder. Eu resolvi meu problema agora, então o acima é principalmente curiosidade - não há pressa em responder.

Não tenho certeza do que você está dizendo exatamente. Qual deve ser a aparência de ModuleA.d.ts nesta proposta?

O compilador não adicionará dependências (isto é, instruções de importação) que não existiam no código do usuário quando ele emite declarações. o erro que você está obtendo significa que o compilador está tentando escrever uma anotação de tipo para uma declaração exportada, mas não conseguiu. isso pode ter uma de duas razões: o nome não está acessível, ou seja, não foi importado no módulo atual, ou há uma declaração que está obscurecendo a declaração original.

em ambos os casos, sua solução é adicionar uma anotação de tipo explícita; se você adicionar qualquer anotação de tipo, ela será emitida literalmente na saída; a outra opção é certificar-se de que o nome está acessível, ou seja, você tem uma importação para o módulo e entende o que isso significa para os usuários que estão importando seu módulo.

@mhegazy disse:

O compilador não adicionará dependências (isto é, instruções de importação) que não existiam no código do usuário quando ele emite declarações.

O problema é que nem sempre preciso das instruções de importação no código (obviamente, já que funciona sem --declarations ) e incluí-las é barulhento, fazendo com que coisas como tslint se queixem de "importação não utilizada". Eu posso ver por que você não gostaria que o compilador adicionasse dependências ao javascript emitido, mas qual é o problema em adicioná-los às declarações de emissão?

sinta-se à vontade para registrar um problema para rastrear esta sugestão. as importações racionais são uma declaração de dependência e o compilador não deve criar uma para você, a menos que você o instrua a fazer.

Isso pode ter consequências mais profundas.

Considere moduleA -> moduleB -> moduleC -> moduleD .

moduleB é aquele que precisa fazer import { ABC } from 'moduleA' para contornar este problema.

Quando moduleC usa moduleB e exporta sua assinatura, ele falha novamente na compilação porque moduleB precisa de ABC de moduleA mas desta vez moduleB não o exportou.

Isso significa:

  1. moduleC precisa ter uma dependência rígida de moduleA e importar ABC
  2. moduleB precisa não apenas importar, mas também reexportar ABC e então moduleC importá-lo.

Se moduleD faz coisas semelhantes, então basicamente você precisa conhecer toda a cadeia de dependências.

Não fui capaz de testar isso para provar que é o caso porque estou usando typings e há um problema que me bloqueia: https://github.com/typings/typings/issues/625 ~~

EDIT: Eu posso reproduzi-lo e de fato meu moduleD precisa fazer referência a moduleA para fazer a importação.
No meu exemplo:

  • moduleA => redux
  • moduleB => redux-thunk
  • moduleC => código personalizado além de redux-thunk
  • moduleD => alguma biblioteca
  • ABC => Dispatch interface em redux

Eu tenho o mesmo problema descrito por @unional

Por favor, reabra este problema, os problemas descritos por @unional são muito realistas e isso torna muito difícil adicionar quaisquer bibliotecas intermediárias / auxiliares em cima das bibliotecas enquanto usamos os mesmos tipos do módulo original que estamos reexportando.

Emita https://github.com/Microsoft/TypeScript/issues/9944 rastreia a adição da importação na fase de emissão da declaração.

Obrigado!

O problema em adicionar a importação é que com noUnusedLocals o compilador reclamará do tipo não sendo usado. Eu poderia adicionar uma anotação de tipo explícita, mas não obtenho inferência. Exemplo:

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

Eu gostaria de omitir a anotação de tipo para ensureFetched

Eu encontrei uma solução alternativa para isso:
no tsconfig: include: [ ..., "node_modules/@your_scope/your_library" ]
boa sorte e divirta-se: smiley:

@ salim7 isso diminui os tempos de compilação (porque você está recompilando uma biblioteca que já deveria ter sido compilada) e força você a usar o mínimo denominador comum de configurações de rigidez com a biblioteca de destino.

@mhegazy, o problema foi reproduzido no próximo cenário:

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();
    }
}

A solução é especificar o tipo explícito:

import * as Foo from "./Foo";

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

Não consigo reproduzir isso usando o exemplo acima. registre um novo bug e forneça mais contexto para poder reproduzir o problema.

@PFight Obrigado! Para mim, chega!

Acabei de enfrentar esse problema ao usar:

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

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

Mas isso realmente não deveria ser necessário tbh.

Alguém descobriu como lidar com noUnusedLocals ?

@yordis // @ts-ignore

@pelotom sim, isso é totalmente minha culpa,

Eu estava pensando em uma coisa e escrevi outra.

A Typescript corrigiu o problema entre noUnusedLocals e as declarações?

Minha solução alternativa atual, que é uma dor total para mim,

import {SomeInterface} from "./SomeFile";

const _dummySomeInterface : undefined|SomeInterface = undefined;
_dummySomeInterface;

//Code that implicitly uses SomeInterface

Evita noUnusedLocals , permite a inferência de tipo para interfaces genéricas também, quando possível.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

remojansen picture remojansen  ·  3Comentários

seanzer picture seanzer  ·  3Comentários

wmaurer picture wmaurer  ·  3Comentários

kyasbal-1994 picture kyasbal-1994  ·  3Comentários

manekinekko picture manekinekko  ·  3Comentários