Um guia abrangente de “Module System” em TypeScript (com exemplos)
On Outubro 16, 2021 by adminModule Standard
EcmaScript padronizou como devemos importar módulos em JavaScript e este padrão gira em torno de duas palavras-chave, import
e export
. Como o sistema de módulos em JavaScript está se tornando popular, há algumas novas mudanças vindo para este padrão a cada ano.
Neste tutorial, vamos olhar para a semântica do sistema de módulos em TypeScript (que na maioria se assemelha ao padrão EcmaScript) e como import
e export
a palavra-chave funciona.
Named Exports
A palavra-chave export
torna um valor (variável) definido em um arquivo disponível para importação em outros arquivos de módulos. Quando um módulo é importado em outro arquivo de módulo usando a palavra-chave import
, o arquivo de importação pode especificar os valores que ele quer acessar do módulo importado.
// program.ts
import { A } from 'path/to/values';console.log( A ); // "Apple"
No exemplo acima, estamos importando values.ts
no arquivo program.ts
e extraindo o membro A
exportado. Isto significa que values.ts
deve exportar o valor A
de alguma forma. Existem várias maneiras de expor A
.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class B {}
function C(){}
enum D{}export { A, B, C, D };
Você pode exportar qualquer valor TypeScript acessível com um identificador válido (nome). Aqui neste exemplo, estamos exportando uma variável A
(poderia também ser uma constante), classe B
, e assim por diante. Usando export {}
sintaxe, você pode exportar quantos valores quiser e qualquer um pode importá-los usando import { A, B }
sintaxe. Você não precisa importar todos os valores exportados.
Há outra maneira de exportar um valor onde ele foi declarado.
// values.ts
export const A = { name: "Apple" };
export class B {}
export function C(){}
export enum D{}
No exemplo acima, nós exportamos todos os valores onde eles foram declarados. Você também não precisa inicializar o valor, você pode fazer isso mais tarde pelo arquivo. Esta é uma maneira muito melhor comparada com a anterior.
Em ambos os casos, todos os seus membros de exportação estão acessíveis dentro do mesmo arquivo, se você precisar usá-los. Ter export
palavra-chave num valor não altera o seu comportamento dentro do ficheiro do módulo.
Padrão de exportação
Até agora exportamos valores que só podem ser importados fornecendo o nome dos membros de exportação como import { A } from 'path/to/values'
onde A
aqui é um membro de exportação de values.ts
. Estes são chamados de exportações nomeadas.
Você também está autorizado a exportar um único valor que pode ser importado sem especificar um nome. Este valor deve ser identificado com default
palavra-chave juntamente com a palavra-chave export
palavra-chave.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class A {}
function A(){}
enum A{}export default A;
Apenas um único valor é permitido ser exportado como exportação padrão, portanto você não tem export default {...}
expressão para trabalhar. Ao contrário das exportações nomeadas, um valor não pode ser exportado como padrão com uma declaração de variável (ou constante), exceto para uma declaração function
e class
.
// values.ts
export default var A = "Apple"; // ❌ invalid syntax
export default enum D{} // ❌ illegal: not a function or class
export default class B {} // ✅ legal
export default function C(){} // ✅ legal
No entanto, você pode exportar um valor diretamente como uma exportação padrão. Isso significa que qualquer expressão JavaScript pode ser exportada como o valor padrão de exportação.
// values.ts
export default "Hello"; // string
export default {}; // object
export default () => undefined; // function
export default function(){}; // function
export default class{}; // class
Desde que a exportação padrão não tenha um nome de exportação, nós podemos fornecer qualquer nome para ela durante a importação. Você pode ter nomeado exportações e uma exportação padrão no mesmo arquivo de módulo. Da mesma forma, você pode importar a exportação padrão e valores de exportação nomeados em uma única declaração import
como mostrado abaixo.
import Apple, { B, C } from 'path/to/values';
No exemplo acima, o Apple
é o nome que usamos para coletar a exportação padrão do arquivo values.ts
. Você pode soltar a expressão { B, C }
ou Apple
nome da declaração import
se não for usá-la.
>
💡 Como as exportações nomeadas, não é obrigatório importar o valor padrão de exportação quando
import
declaração é usada. Mas, oApple
deve vir antes das exportações nomeadas na declaraçãoimport
signature.
Você também pode usar as default
syntax no export {}
signature para exportar um valor como padrão junto com outras exportações nomeadas. Isto é chamado aliasing e é explicado na próxima seção.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class B {}
function C(){}
enum D{}export { A as default, B, C, D };
💡 O motivo pelo qual eu não uso e recomendo
default
exportação tem a ver com a experiência do desenvolvedor. Como aprendemos, você pode fornecer qualquer nome para o memeber padrão de exportação de um módulo na declaraçãoimport
. Se este valor for importado em vários arquivos, você pode referenciá-lo com qualquer nome de sua escolha e ter nomes diferentes para o mesmo membro de exportação do mesmo módulo é um DX.
Import And Export Alias
No caso da exportação padrão, você pode referenciar o valor com qualquer nome de sua escolha na declaração import
. Contudo, esse não é o caso das exportações com nome. Entretanto, você pode usar as
palavra-chave para referenciar um valor de exportação nomeado com o nome de sua escolha.
// program.ts
import A, { B as Ball } from 'path/to/values';console.log( B ); // ❌ Error: Cannot find name 'B'.
console.log( Ball ); // ✅ legal
No exemplo acima, estamos importando B
do values.ts
mas ele foi renomeado para Ball
. Assim, no arquivo program.ts
, você estaria usando Apple
ao invés de B
. Quando você é um membro de exportação, o nome original não existe mais no escopo global, portanto B
não existirá mais no arquivo program.ts
.
💡 Você não pode ser o membro de exportação padrão, já que você já pode fornecer qualquer nome de sua escolha, mas você pode usar
import { default as defaultValue, }
.
Você também tem permissão para apelidar um valor enquanto exporta usando as
palavra-chave.
// values.ts
var A = "Apple"; // can also use `let` or `const`
class B {}
function C(){}
enum D{}export { A as default, B, C as Cat, D };
No exemplo acima, nós exportamos a variável A
como exportação padrão e função C
como Cat
. Portanto, qualquer um que importar esses valores de um arquivo values.ts
deve usar a sintaxe padrão de importação para A
e Cat
para o arquivo C
valor.
Import All Named Exports
Você pode importar todas as exportações nomeadas de um arquivo usando a sintaxe * as
.
// program.ts
import Apple, * as values from 'path/to/values';console.log( values.B );
console.log( values.C );
console.log( values.D );
Aqui, todas as exportações nomeadas de values.ts
serão salvas em values
que será um objeto onde o keys
será o nome dos membros de exportação e values
serão os valores exportados dos membros de exportação.
Reexportação
Um valor importado pode ser reexportado da forma normal. Quando você importa algo, você tem uma referência para o valor de importação e pode usar o mesmo valor na sintaxe export
. Nada está errado com isso.
// lib.ts
import A, { B, C as Cat, D } from 'path/to/values';export { D as default, A, B, Cat as C, D };
No exemplo acima, você importou alguns valores de values.ts
dentro de lib.ts
e exportou de acordo com a sua preferência. Isso é ótimo se você quiser usar essas importações em lib.ts
assim como exportar algumas delas se outro arquivo precisar delas quando importar do arquivo lib.ts
.
No entanto, também temos export ... from
comando para exportar valores de um arquivo sem ter que importá-los primeiro. Isso é ótimo quando você quer manter um único ponto de entrada para todas as importações em seu módulo.
// lib.ts// re-exports
export { P as Paper, Q } from 'path/to/values-1';
export { N, O as Orange } from 'path/to/other/values-2';// default export
export default "hello";class B {}
function C(){}// named exports
export { B, C as Cat };
No exemplo acima, estamos exportando alguns dos membros de exportação de values-1.ts
e values-2.ts
do arquivo lib.ts
(exceto a exportação padrão). Também podemos usar o aliasing no arquivo export ... from
syntax.
A sintaxe de reexportação não afeta o arquivo atual, portanto você pode ter suas exportações regulares no arquivo, assim como mostrado acima. Ao contrário de export
palavra-chave, export ... from
sintaxe não permite o acesso aos membros reexportados. Assim você não poderá acessar P
ou Orange
no arquivo lib.ts
.
// lib.ts
export { P as Paper, Q } from 'path/to/values-1';
export { N, O as Orange } from 'path/to/other/values-2';console.log( P ); // ❌ Error: Cannot find name 'P'.
console.log( Orange ); // ❌ Error: Cannot find name 'Orange'.
Você pode exportar todas as exportações nomeadas de um arquivo usando export * from
sintaxe, pois nós como você tem a capacidade de renomeá-las enquanto exporta usando export * as ... from
.
// lib.ts
export * from 'path/to/values-1';
export * as values2 from 'path/to/other/values-2';
Do exemplo acima, se values-1.ts
exporta P
e Q
, então qualquer um pode importar P
e Q
de lib.ts
. Entretanto, todas as exportações nomeadas de values-2.ts
são reportadas como values2
exportação nomeada de lib.ts
, portanto você precisa importá-las usando a seguinte sintaxe.
// program.ts
import { P, Q, values2 } from 'path/to/lib';
Você não pode acessar a exportação padrão usando export ... from
sintaxe de uma forma normal. Mas você pode exportar a exportação padrão usando a palavra-chave default
.
// lib.ts
export { default } from 'path/to/values-1';
export { default as Apple } from 'path/to/values-2';
No exemplo acima, a exportação padrão de lib.ts
é o valor padrão de exportação de values-2.ts
. O valor padrão de exportação de values-2
será exportado de lib.ts
como o valor Apple
chamado export.
Importar para efeito lateral
Pode você imaginar importar um arquivo sem especificar os membros exportados?
import 'path/to/action';
No exemplo acima, estamos importando action.ts
mas especificamos qualquer membro exportado por action.ts
. Esta é uma sintaxe válida mas é usada para produzir um resultado muito diferente.
Quando você importa um arquivo usando a palavra-chave import
, ele é primeiro executado e depois todos os membros exportados estão disponíveis para importação. Assim, no exemplo abaixo, um arquivo importando PI
obteria o 3.14
como um floating-point
number.
export const PI = parseFloat( "3.14" ); // 3.14
Seguir a mesma lógica, se você quiser criar um efeito colateral importando um arquivo, então você pode fazer isso totalmente. Então, por exemplo, se existe um arquivo que inicializa algumas variáveis globais, você pode importar isso sem especificar os membros exportados desse arquivo (ele também poderia exportar nenhum em primeiro lugar).
Compilador de Tipo-Script pode criar um arquivo de pacote JavaScript (.js
) compilando dois ou mais arquivos .ts
juntos. Isso normalmente é feito fornecendo um arquivo TypeScript de entrada e depois passando pela sua árvore de dependências.
Uma árvore de dependências é construída olhando para todas as import
declarações (dependências) do arquivo de entrada e seguindo as dependências desses arquivos importados. Se você quiser incluir um arquivo cujo código pode criar alguns efeitos colaterais no momento da execução, então você deve importar esse arquivo sem especificar os membros exportados na sintaxe import
. Isso pode ser feito no arquivo de entrada ou dentro de qualquer arquivo que esteja dentro da árvore de dependências.
Deixe uma resposta