Candy Cup Analogy
Versão 1: Um copo para cada doce
Digamos que você escreveu um código como este:
Mod1.ts
export namespace A {
export class Twix { ... }
}
Mod2.ts
export namespace A {
export class PeanutButterCup { ... }
}
Mod3.ts
export namespace A {
export class KitKat { ... }
}
Você criou esta configuração:
Cada módulo (folha de papel) recebe o seu próprio copo chamado A
. Isso é inútil - você não está realmente organizando seu doce aqui, apenas adicionando uma etapa adicional (tirando-a do copo) entre você e as guloseimas.
Versão 2: um copo no escopo global
Se você não estava usando módulos, você pode escrever um código como este (observe a falta de export
declarações):
global1.ts
namespace A {
export class Twix { ... }
}
global2.ts
namespace A {
export class PeanutButterCup { ... }
}
global3.ts
namespace A {
export class KitKat { ... }
}
Este código cria um espaço A
para nome mesclado no escopo global:
Essa configuração é útil, mas não se aplica aos módulos (porque os módulos não poluem o escopo global).
Versão 3: Indispensável
Voltando ao exemplo original, os copos A
, A
e A
não está fazendo nenhum favor. Em vez disso, você pode escrever o código como:
Mod1.ts
export class Twix { ... }
Mod2.ts
export class PeanutButterCup { ... }
Mod3.ts
export class KitKat { ... }
para criar uma imagem parecida com esta:
Muito melhor!
Agora, se você ainda está pensando em quanto realmente deseja usar o espaço para nome com seus módulos, continue lendo ...
Estes não são os conceitos que você está procurando
Precisamos voltar às origens do porque os namespaces existem em primeiro lugar e examinar se esses motivos fazem sentido para módulos externos.
Organização : os namespaces são úteis para agrupar objetos e tipos relacionados à lógica. Por exemplo, em C #, você encontrará todos os tipos de coleção System.Collections
. Ao organizar nossos tipos em namespaces hierárquicos, fornecemos uma boa experiência de "descoberta" para usuários desses tipos.
Conflitos de nome : os espaços para nome são importantes para evitar colisões de nomes. Por exemplo, você pode ter My.Application.Customer.AddForm
e My.Application.Order.AddForm
- dois tipos com o mesmo nome, mas um espaço para nome diferente. Em um idioma em que todos os identificadores existem no mesmo escopo raiz e todos os assemblies carregam todos os tipos, é essencial ter tudo em um espaço para nome.
Esses motivos fazem sentido em módulos externos?
Organização : Módulos externos já estão presentes em um sistema de arquivos, necessariamente. Temos que resolvê-los por caminho e nome de arquivo, para que exista um esquema de organização lógica para usar. Podemos ter uma /collections/generic/
pasta com um list
módulo.
Conflitos de nome : isso não se aplica em todos os módulos externos. Dentro de um módulo, não há razão plausível para ter dois objetos com o mesmo nome. Do lado do consumo, o consumidor de qualquer módulo pode escolher o nome que usará para se referir ao módulo, portanto, conflitos de nomes acidentais são impossíveis.
Mesmo que você não acredite que esses motivos sejam tratados adequadamente pela maneira como os módulos funcionam, a "solução" de tentar usar espaços para nome em módulos externos nem funciona.
Caixas em Caixas em Caixas
Uma história:
Seu amigo Bob liga para você. "Eu tenho um ótimo novo esquema de organização em minha casa", diz ele, "venha conferir!". Legal, vamos ver o que Bob inventou.
Você começa na cozinha e abre a despensa. Existem 60 caixas diferentes, cada uma rotulada como "Despensa". Você escolhe uma caixa aleatoriamente e abre-a. Dentro, há uma única caixa chamada "Grãos". Você abre a caixa "Grãos" e encontra uma única caixa chamada "Macarrão". Você abre a caixa "Pasta" e encontra uma única caixa chamada "Penne". Você abre esta caixa e encontra, como esperado, um saco de macarrão penne.
Um pouco confuso, você pega uma caixa adjacente, também chamada de "Despensa". Dentro, há uma única caixa, novamente rotulada como "Grãos". Você abre a caixa "Grãos" e, novamente, encontra uma única caixa chamada "Macarrão". Você abre a caixa "Pasta" e encontra uma única caixa, esta é rotulada "Rigatoni". Você abre esta caixa e encontra ... um saco de macarrão rigatoni.
"É ótimo!" diz Bob. "Tudo está em um espaço para nome!".
"Mas Bob ..." você responde. "O esquema da sua organização é inútil. Você precisa abrir um monte de caixas para chegar a qualquer coisa, e não é realmente mais conveniente encontrar alguma coisa do que se você tivesse colocado tudo em uma caixa em vez de três . De fato, desde o seu a despensa já está classificada de prateleira a prateleira, você não precisa das caixas. Por que não colocar a massa na prateleira e buscá-la quando precisar? "
"Você não entende - preciso garantir que ninguém mais coloque algo que não pertença ao espaço para nome 'Despensa'. E organizei com segurança toda a minha massa no Pantry.Grains.Pasta
espaço para nome, para que eu possa encontrá-lo facilmente"
Bob é um homem muito confuso.
Os módulos são sua própria caixa
Você provavelmente teve algo semelhante acontecendo na vida real: você pede algumas coisas na Amazon e cada item aparece em sua própria caixa, com uma caixa menor dentro, com o item embrulhado em sua própria embalagem. Mesmo se as caixas internas forem semelhantes, as remessas não serão úteis "combinadas".
Seguindo a analogia da caixa, a principal observação é que os módulos externos são sua própria caixa . Pode ser um item muito complexo, com muitas funcionalidades, mas qualquer módulo externo é sua própria caixa.
Orientação para módulos externos
Agora que descobrimos que não precisamos usar 'namespaces', como devemos organizar nossos módulos? Seguem alguns princípios e exemplos orientadores.
Exporte o mais próximo possível do nível superior
- Se você estiver exportando apenas uma única classe ou função, use
export default
:
MyClass.ts
export default class SomeType {
constructor() { ... }
}
MyFunc.ts
function getThing() { return 'thing'; }
export default getThing;
Consumo
import t from './MyClass';
import f from './MyFunc';
var x = new t();
console.log(f());
Isso é ideal para os consumidores. Eles podem nomear seu tipo como quiserem ( t
nesse caso) e não precisam fazer pontuações estranhas para encontrar seus objetos.
- Se você estiver exportando vários objetos, coloque-os todos no nível superior:
MyThings.ts
export class SomeType { ... }
export function someFunc() { ... }
Consumo
import * as m from './MyThings';
var x = new m.SomeType();
var y = m.someFunc();
- Se você estiver exportando um grande número de coisas, só então use a palavra-chave
module
/ namespace
:
MyLargeModule.ts
export namespace Animals {
export class Dog { ... }
export class Cat { ... }
}
export namespace Plants {
export class Tree { ... }
}
Consumo
import { Animals, Plants} from './MyLargeModule';
var x = new Animals.Dog();
Bandeiras vermelhas
Todos os seguintes são sinais de alerta para a estruturação do módulo. Verifique se você não está tentando nomear seus módulos externos, se algum deles se aplicar aos seus arquivos:
- Um arquivo cuja única declaração de nível superior é
export module Foo { ... }
(remova Foo
e mova tudo 'para cima' em um nível)
- Um arquivo que possui um único
export class
ou export function
que não éexport default
- Vários arquivos com o mesmo
export module Foo {
nível superior (não pense que eles serão combinados em um Foo
!)
Nada de errado com a resposta de Ryan, mas para as pessoas que vieram aqui procurando como manter uma estrutura de uma classe por arquivo enquanto ainda usam os espaços para nome ES6 corretamente, consulte este recurso útil da Microsoft.
Uma coisa que não está clara para mim depois de ler o documento é: como importar o módulo inteiro (mesclado) com um único
import
.Edite Circulando de volta para atualizar esta resposta. Algumas abordagens para namespacing surgem no TS.
Todas as classes de módulo em um arquivo.
Importar arquivos para o espaço para nome e reatribuir
Barris
Uma consideração final. Você pode namespace cada arquivo
Mas, como se importa duas classes do mesmo espaço para nome, o TS reclama que há um identificador duplicado. A única solução, neste momento, é alias do espaço para nome.
Esse apelido é absolutamente repugnante, então não faça isso. Você está melhor com uma abordagem acima. Pessoalmente, eu prefiro o 'barril'.
fonte
const fs = require('fs')
,fs
é o espaço para nome.import * as moment from 'moment'
,moment
é o espaço para nome. Isso é ontologia, não a especificação.require
exemplo não se aplica a eles por vários motivos, incluindo que os namespaces ES6 não podem ser chamados, enquantorequire
retorna um objeto simples que pode ser chamado .Tente organizar por pasta:
baseTypes.ts
dog.ts
tree.ts
LivingThings.ts
main.ts
A idéia é que seu próprio módulo não se importe / saiba que está participando de um espaço para nome, mas isso expõe sua API ao consumidor de uma maneira compacta e sensível, independente do tipo de sistema de módulo que você está usando para o projeto.
fonte
tree.ts
quando ele não possui nenhum membro exportado?import
erequire
juntos em um comunicado.Pequeno impulso da resposta Albinofrenchy:
base.ts
dog.ts
things.ts
main.ts
fonte
OP estou com você, cara. novamente, também, não há nada errado com essa resposta com mais de 300 votos, mas minha opinião é:
o que há de errado em colocar as classes em seus próprios arquivos aconchegantes e aconchegantes individualmente? Quero dizer, isso fará com que as coisas pareçam muito melhores, certo? (ou alguém como um arquivo de 1000 linhas para todos os modelos)
então, se o primeiro for alcançado, temos que importar import import ... import apenas em cada um dos arquivos de modelo, como man, srsly, um arquivo de modelo, um arquivo .d.ts, por que existem tantos * está aí? deve ser simples, arrumado e é isso. Por que preciso de importações para lá? porque? C # tem namespaces por um motivo.
E então, você está literalmente usando "filenames.ts" como identificadores. Como identificadores ... Vamos no 2017 agora e ainda fazemos isso? Vou voltar a Marte e dormir por mais 1000 anos.
Por isso, infelizmente, minha resposta é: não, você não pode tornar funcional o "namespace" se não usar todas essas importações ou usar esses nomes de arquivos como identificadores (o que eu acho realmente tolo). Outra opção é: coloque todas essas dependências em uma caixa chamada filenameasidentifier.ts e use
envolva-os para que eles não tentem acessar outras classes com o mesmo nome quando estiverem apenas tentando obter uma referência da classe, bem em cima deles.
fonte
Várias das perguntas / comentários que eu vi sobre esse assunto me parecem como se a pessoa estivesse usando o
Namespace
que significa 'apelido de módulo'. Como Ryan Cavanaugh mencionou em um de seus comentários, você pode ter um módulo 'Wrapper' reexportando vários módulos.Se você realmente deseja importar tudo do mesmo nome / alias do módulo, combine um módulo wrapper com um mapeamento de caminhos no seu
tsconfig.json
.Exemplo:
./path/to/CompanyName.Products/Foo.ts
./path/to/CompanyName.Products/Bar.ts
./path/to/CompanyName.Products/index.ts
tsconfig.json
main.ts
Nota : A resolução do módulo nos arquivos .js de saída precisará ser tratada de alguma forma, como neste https://github.com/tleunen/babel-plugin-module-resolver
Exemplo
.babelrc
para lidar com a resolução de alias:fonte
Experimente este módulo de namespaces
namespaceModuleFile.ts
bookTreeCombine.ts
--- parte da compilação ---
fonte
dog.ts
tree.ts
fonte
A maneira correta de organizar seu código é usar diretórios separados no lugar de namespaces. Cada classe estará em seu próprio arquivo, na respectiva pasta de namespace. index.ts somente reexporta cada arquivo; nenhum código real deve estar no arquivo index.ts. Organizar seu código assim facilita muito a navegação e é auto-documentado com base na estrutura de diretórios.
Você usaria então:
fonte