Módulos

Módulos

Com a chegada do ES6, o Javascript passou a ter suporte a importação e exportação de arquivos por meio dos comandos import e export.

Isso nos possibilitou a criação de módulos dentro da nossa aplicação, o que é uma estratégia essêncial para separarmos cada vez mais a lógica em pequenos componentes.

Nesta lição, nós iremos aprender a utilização desses módulos no Typescript, vamos nessa? 

O que são Módulos?

Pense em módulos como pequenas partes de um grande quebra cabeça, que quando juntas formam algo maior.

De uns tempos pra cá, cada vez mais as aplicações web vem se "modularizando", isso vem acontecendo desde separação do front-end e back-end, até a criação de microserviços.

A biblioteca do ReactJS, por exemplo, nos instrui a criar aplicações modularizadas, onde temos uma clara separação do que são componentes, serviços, rotas, contextos e afins.

Será por meio dos comandos import e export, que o Typescript conseguirá fazer a importação de algumas partes da nossa aplicação para dentro de outras.

Como os módulos funcionam?

Para importar um módulo para dentro de um determinado arquivo, usamos o comando import da seguinte forma:

import './arquivo.css';//Exemplo de uma importação de um arquivo .css

import './index.ts'//Exemplo de uma importação de um arquivo .ts

Além disso, caso o arquivo que está sendo importado estiver usufruindo do comando export, nós podemos importar nossos módulos informando um determinado nome para ele:

import Home from './home.ts';

import { Page, Delete, Loop } from './modulo-que-exporta-diversos-arquivos.ts';

Já com relação aos exports, você pode exportar uma determinada função, classe ou variável da seguinte forma:

export function olaMundo(nome: string): string {
 return `Olá, ${nome}!`;
}

No comando acima nós estamos exportando uma função chamada olaMundo()usando o comando export.

Para importá-la em outro arquivo você pode fazer isso da seguinte forma:

import { olaMundo } from './meu-arquivo.ts';

console.log(olaMundo('Micilini'));

Dessa forma fica bem mais fácil separarmos cada lógica dentro da nossa aplicação 😉

Agora que você já sabe a base do funcionamento dos módulos em Typescript, vamos nos aprofundar um pouco mais.

Exportando arquivos

Tudo começa com um determinado arquivo, na qual você deseja exportar parte da sua lógica, para que ela seja acessível em outro arquivo.

E como dito anteriormente, a exportação de arquivos é feita com o uso do comando export ou export default.

Mas qual a real diferença entre esses dois? (Se é que tem rs)

No caso do export ele é conhecido no mundo da programação como uma exportação nomeada, o que significa que com este comando você pode  exportar várias funções, classes e variáveis que existem dentro de um mesmo arquivo.

Por exemplo, vamos supor que você tem um arquivo chamado de utilitarios.ts que contém diversas funções declaradas dentro dele:

function adicionaSobrenome(nome: string): string{
 return nome + ' ' + 'Da Silva';
}

function pegaPrimeiraLetra(texto: string): string {
 return texto.length > 0 ? texto.charAt(0) : '';
}

function contadorDeCaracteres(texto: string): number {
 return texto.length;
}

Cada uma das funções acima, realizam operações diferentes, a primeira adiciona o sobrenome "da silva" a um determinado nome, a segunda retorna a primeira letra de um texto, e a última realiza a contagem de caracteres de um texto.

Supondo que você queira exportar todas essas funções para que elas possam ser acessadas por outros arquivos, você poderia fazer isso da seguinte forma:

export function adicionaSobrenome(nome: string): string{
 return nome + ' ' + 'Da Silva';
}

export function pegaPrimeiraLetra(texto: string): string {
 return texto.length > 0 ? texto.charAt(0) : '';
}

export function contadorDeCaracteres(texto: string): number {
 return texto.length;
}

Em seguida, você poderá usá-las em qualquer arquivo por meio do import, por exemplo:

import { adicionaSobrenome, pegaPrimeiraLetra, contadorDeCaracteres } from './utilitarios.ts';

console.log(adicionaSobrenome('João'));// "João Da Silva"
console.log(pegaPrimeiraLetra('Ana'));// "A"
console.log(contadorDeCaracteres('Maria'));// 5

Com isso, nós temos uma flexibilidade maior, de modo que podemos separar as responsabilidades em arquivos diferentes.

Portanto, podemos dizer que usamos o comando export para exportar múltiplos itens, de modo que o nome da exportação seja explícito na importação.

Além disso, quando temos multiplas exportações dentro de um mesmo arquivo, o import deverá ser feito por meio de chaves ({}).

Tenha em mente que nem sempre precisamos importar todas as funções, você pode importar somente aquelas que forem necessários, por exemplo:

import { adicionaSobrenome } from './funcoes';

console.log(adicionaSobrenome('João'));// "João Da Silva"

Legal, agora partindo para o export default, como o próprio nome já nos diz, ele é um tipo de exportação padrão, que permite exportar um único item como o valor padrão de um módulo.

Isso significa que ao importar o módulo, você pode usar qualquer nome para o item exportado.

Por exemplo, vamos supor que nós temos um outro arquivo chamado de olaMundo.ts:

export default function olaMundo(nome: string): string {
 return `Olá, ${nome}!`;
}

Note que estamos usando o comando export default, indicando que ele exporta apenas um item por módulo.

Para usar esse módulo, você não precisa das chaves ({}), bastando apenas usar a nomenclatura que quiser:

import olaMundo from './olaMundo.ts';//Como convenção, é recomendável que você importe usando o mesmo nome da função do export default.

console.log(greet('Micilini'));

Exportações Mistas

Você também pode exportar funções, variáveis e módulos usando export e export default dentro de um mesmo arquivo, veja como é fácil:

export function olaMundo(nome: string): string {
 return `Olá, ${nome}!`;
}

export const pi = 3.14159;

export default class Pessoa {
 constructor(public nome: string) {}

 boasVindas(): string {
 return `Olá, meu nome é ${this.nome}.`;
 }
}

Agora observe como pode ser feita a importação:

import Pessoa, { olaMundo, pi } from './modulo-misto.ts';

console.log(olaMundo('Micilini'));
console.log(pi);

const pessoa = new Pessoa('Roll');
console.log(pessoa.boasVindas());

Sendo assim, nós podemos dizer que:

Use export para exportar múltiplos itens de um determinado módulo, e quando você quer que o nome da exportação seja explícito na importação.

Use export default quando você quer exportar um único item, e permitir que o nome da importação seja escolhido pelo usuário do módulo.

Usando o Export Default no final do arquivo

É uma prática bastante comum, principalmente quem trabalha criando módulos no ReactJS ou no NodeJS, em que se usa o export default sempre no final do arquivo.

Isso pode ser feito da seguinte forma:

function olaMundo(nome: string): string {
 return `Olá, ${nome}!`;
}

export default olaMundo;

A mesma coisa pode ser feita por meio do export, observe:

function adicionaSobrenome(nome: string): string {
 return nome + ' ' + 'Da Silva';
}

function pegaPrimeiraLetra(texto: string): string {
 return texto.length > 0 ? texto.charAt(0) : '';
}

function contadorDeCaracteres(texto: string): number {
 return texto.length;
}

// Exportações
export { pegaPrimeiraLetra, contadorDeCaracteres };

// Exportação padrão
export default adicionaSobrenome;

Legal, não acha?

Usando Alias de Importação

Haverá momentos em que você deseja mudar o nome da função que esta sendo importada.

E você pode fazer isso da seguinte forma:

function adicionaSobrenome(nome: string): string {
 return nome + ' ' + 'Da Silva';
}

export default adicionaSobrenome;

....

import adicionarNomeCompleto from './funcoes';

console.log(adicionarNomeCompleto('João')); // "João Da Silva"

....

Além disso, caso desejar você mudar a nomeclatura usando o comando as junto com export:

export function adicionaSobrenome(nome: string): string {
 return nome + ' ' + 'Da Silva';
}

export function pegaPrimeiraLetra(texto: string): string {
 return texto.length > 0 ? texto.charAt(0) : '';
}

....

import { adicionaSobrenome as adicionarNomeCompleto, pegaPrimeiraLetra } from './funcoes';

console.log(adicionarNomeCompleto('João')); // "João Da Silva"
console.log(pegaPrimeiraLetra('Ana'));// "A"

No comando acima mudamos a chamada de adicionaSobrenome para adicionarNomeCompleto.

Usando o asterísco (*)

O * é usado na importação para importar todos os itens exportados de um módulo como um objeto.

Esse método é conhecido como "importação de tudo", e cria um namespace que contém todas as exportações do módulo.

Vejamos como isso funciona na prática:

export function adicionaSobrenome(nome: string): string {
 return nome + ' ' + 'Da Silva';
}

export function pegaPrimeiraLetra(texto: string): string {
 return texto.length > 0 ? texto.charAt(0) : '';
}

export function contadorDeCaracteres(texto: string): number {
 return texto.length;
}

Supondo que você queira importar todas as funções sem precisar ficar digitando o nome de cada uma delas durante a importação, podemos usar a seguinte sintaxe com o asterísco (*):

import * as funcoes from './funcoes';

console.log(funcoes.adicionaSobrenome('João')); // "João Da Silva"
console.log(funcoes.pegaPrimeiraLetra('Ana')); // "A"
console.log(funcoes.contadorDeCaracteres('Maria')); // 5

Dessa forma, conseguimos usar o alias funcoes, para acessar cada uma das funções que foram exportadas pelo nosso módulo.

Exportando Types e Interfaces

Em lições passadas, você aprendeu a fazer o uso de Types e Interfaces.

Mas será que é possível exportar esses objetos? Sim, observe:

// Definindo uma interface
export interface Pessoa {
 nome: string;
 idade: number;
}

// Definindo um tipo
export type Produto = {
 nome: string;
 preco: number;
};

// Definindo uma função que usa uma interface
export function apresentarPessoa(pessoa: Pessoa): string {
 return `Nome: ${pessoa.nome}, Idade: ${pessoa.idade}`;
}

Para fazer a importação, também é bem simples, observe:

import { Pessoa, Produto, apresentarPessoa } from './tipos';

// Usando a interface
const pessoa: Pessoa = {
 nome: 'João',
 idade: 30
};

// Usando o tipo
const produto: Produto = {
 nome: 'Notebook',
 preco: 2999.99
};

// Usando a função
console.log(apresentarPessoa(pessoa)); // "Nome: João, Idade: 30"

Essa estratégia de exportar Types e Interfaces é bastante utilizadas em frameworks e bibliotecas, e acredito que você vai ver essa estratégia com bastante frequência na sua jornada 😉

Arquivos da lição

Os arquivos desta lição podem ser encontrados no repositório do GitHub por meio deste link.

Conclusão

Nesta lição você aprendeu a fazer o uso dos comandos import, export e export default do ES6 do Javascript.

Até a próxima lição.