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?
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:
moduleC
precisa ter uma dependência rígida de moduleA
e importar ABC
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 bibliotecaABC
=> 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.
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 aclassA
, então é o tipo "não pode ser nomeado" e há um erro.Se você adicionar
import
deClassA
aModuleA.ts
, o erro desaparecerá.