Na primeira parte desta série nós vimos o básico de TypeScript e como utilizar ele para adicionar tipagem em parâmetros, retornos de função e até para garantir segurança maior com o strict mode.
No artigo de hoje, vamos avançar nos nossos estudos deste superset tão famoso para TypeScript.
Atenção: este é um post intermediário, para alguém que já tem conhecimentos básicos em Node.js. Ele já vai direto ao ponto, para quem precisa de algo com mais didática, recomendo começar com esse.
Caso prefira, pode assistir ao vídeo abaixo ao invés de ler o tutorial, o conteúdo é semelhante.

Vamos lá!
#1 – Union Types
Falamos de tipagem estática para parâmetros e retorno de funções no último artigo e também do coringa ‘any’ quando queremos manter a tipagem dinâmica, mesmo como strict mode ligado em nosso tsconfig.json. É importante reforçar que any é um anti-pattern, ok? Você deve evitar usá-lo pois ela meio que “desliga” o TypeScript.
Mas e se queremos sim ter tipos, mas mais de um? Algumas linguagens com tipagem forte permite a chamada sobrecarga de métodos (overload), inexistente no JavaScript.
O TypeScript permite o uso de Union Types, que nada mais são do que diferentes tipos, mas ainda estáticos para suas variáveis, como abaixo.
|
1 2 3 4 5 6 7 8 |
function somar(num1 : number | string, num2: number){ if(typeof num1 === 'number') return num1 + num2; else return `${num1}${num2}`; } |
Note que defini mais de um tipo para o parâmetro num1, através do uso do pipe (‘|’). No entanto, o TypeScript me obriga a fazer checagem de tipo com um if quando uso Union Types, como você pode ver que fiz no código acima.
Se eu não fizer, ele irá me gerar um erro.
Ou seja, ele me acaricia com uma mão, e me bate com a outra, hehe.
Ainda na linha de Union Types, o TypeScript possui os Type Aliases, que são apelidos para Union Types.
Assim, posso criar um novo tipo a partir de dois outros possíveis, como abaixo, usando a keyword nova do TS chamada type.
|
1 2 3 4 5 6 7 8 9 |
type NumberOrString = number | string; function somar2(num1 : NumberOrString, num2: number){ if(typeof num1 === 'number') return num1 + num2; else return `${num1}${num2}`; } |
Assim, onde antes eu usaria o Union Type, agora posso usar o Type Alias que chamei de NumberOrString (bem sugestivo).
#2 – Tipos de objetos
Até aqui ficamos mexendo apenas com variáveis simples como números e strings. Mas e quanto aos objetos? Como fazer que a estrutura e tipo dos objetos seja respeitada utilizando TypeScript?
Quando você passa um objeto por parâmetro, você pode definir a sua estrutura no mesmo lugar em que declararia o seu tipo, como abaixo.
|
1 2 3 4 5 6 |
function cadastrar(pessoa: { nome: string, dataNascimento: Date}){ console.log(pessoa.nome); console.log(pessoa.dataNascimento); } |
Note que o código é extremamente autoexplicativo: a função cadastrar espera receber um objeto cliente com um nome (string) e uma data de nascimento (Date).
Aqui, com apenas este trecho de código, não vai ficar evidente uma das maiores vantagens desta abordagem e programação com JS que é o ganho com o auto-complete do VS Code.
Uma vez que eu defini a estrutura do meu objeto, o VS Code consegue me sugerir as suas propriedades quando eu estou manipulando o mesmo no seu interior, coisa que geralmente não aconteceria, me forçando a decorar as informações ou ficar consultando frequentemente.
Essa vantagem da tipagem estática, para mim, supera as demais.
E ela pode ir além, juntando com o conceito de Type Alias que mostrei na seção anterior, que eu posso usar para definir um novo tipo de objeto, como abaixo.
|
1 2 3 4 5 6 7 |
type Pessoa = { nome: string, dataNascimento: Date }; function cadastrar2(pessoa: Pessoa){ console.log(pessoa.nome); console.log(pessoa.dataNascimento); } |
Olha que bacana, criamos uma struct à “moda C”, hehehe.
Mas tem outro objeto que ainda não falamos que também se beneficia da segurança fornecida pela tipagem estática que são os arrays.
Em TypeScript, podemos definir o tipo de dado que deve estar presente no array desta forma.
|
1 2 3 4 |
const numeros : number[] = []; numeros.push(4); |
Aqui estou dizendo que a constante ‘numeros’ é do tipo ‘number[]’ (number array), ou seja, que somente numbers podem ser armazenados no seu interior, acabando com aquela festa do array de qualquer coisa e tornando seu código mais error proof.
Outra forma de fazer a mesma declaração seria como abaixo, usando a classe Array e Generics, um conceito que vou explorar melhor mais à frente.
|
1 2 3 4 |
const numeros : Array<number> = []; numeros.push(4); |
Vamos em frente.
#3 – Intersection Types
Enquanto nos Union Types nós temos uma relação de OR (um tipo OU outro tipo), explícito até mesmo pelo uso do operador “|”, quando queremos literalmente juntar dois tipos para criar um terceiro nós temos o que chamamos de Intersection Types, usando o operador “&”.
|
1 2 3 4 5 6 7 8 |
type Cliente = Pessoa & { email: string }; function cadastrar3(cliente: Cliente){ console.log(cliente.nome); console.log(cliente.dataNascimento); console.log(cliente.email); } |
Veja como a própria leitura do & como AND ajuda a entender: Cliente é um tipo com tudo que Pessoa possui AND email. Isso cria um novo tipo que podemos usar e embora eu tenha feito a intersecção com um type sem nome, obviamente funciona com Type Aliases nos dois lados da operação.
#4 – Compilando
Uma coisa que pode estar incomodando você é que você vai estar o tempo todo programando em arquivos TS e compilando eles em JS, criando não apenas o trabalho de compilação, mas também a bagunça de arquivos praticamente duplicados por todo o projeto, certo?
Aqui vão duas boas práticas de compilação.
A primeira é abrir o seu tsconfig e alterar o diretório padrão onde os arquivos compilados serão armazenados.
|
1 2 3 |
"outDir": "./dist/", |
A sugestão acima é que os arquivos JS sejam compilados e armazenados em uma pasta dist na raiz do projeto.
Agora, para compilar tudo que programamos nesse projeto e ver se essa config surtiu efeito, apenas navegue até a raiz do projeto e rode o comando npx tsc nela, sem nenhum parâmetro adicional.
Isso irá compilar todos os arquivos TS do seu projeto e colocar os equivalentes JS, com a mesma estrutura de pastas original, dentro da pasta dist.
É essa pasta dist que contém a sua aplicação compilada em JS e pronta para execução. Recomendo que coloque ela no seu .gitignore porque não há necessidade de ter ela versionada, pois toda vez que rodar tsc ela será atualizada.
E por fim, também recomendo que configure o script de start corretamente no seu package.json. Incluo aqui também scripts para facilitar a compilação.
|
1 2 3 4 5 6 |
"scripts": { "start": "node dist/app.js", "build": "npx tsc" }, |
Assim, para executar nosso projeto, bastará escrever npm start na raiz do projeto e ele vai se achar corretamente.
Outra dica comum de organização é, assim como criamos uma pasta dist para os arquivos compilados em JS, crie uma pasta src para guardar os fontes TS do projeto, mantendo na raiz do projeto somente os arquivos de configuração e a pasta node_modules, como na imagem.

Para que o compilador do TS não se perca, isso exige que você edite outra configuração no tsconfig.json, como abaixo.
|
1 2 3 |
"rootDir": "./src/", |
A rootDir define o diretório raiz dos seus fontes em TS, necessária para o compilador se achar na hora que você mandar o comando tsc na raiz do projeto.
Recomendo agora que execute esta aplicação e realize os testes.
#5 – Autocompilação
Você deve imaginar que mesmo com o comando tsc na raiz do projeto compilando tudo, é bem chato ter de ficar chamando este comando entre uma alteração e outra antes de reexecutar sua aplicação, certo?
Uma estratégia bem simples durante o desenvolvimento é instalar o pacote TSX.
|
1 2 3 |
npm i -D tsx |
O TSX permite que eu execute o código TypeScript diretamente, sem precisa transpilar (build) e depois executar (start). Obviamente acontece uma transpilação internamente, o que torna este método mais lento de executar que o código JS transpilado, mas do ponto de vista de desenvolvimento é muito mais prático porque não precisa ficar buildando antes de cada teste, apenas uma vez antes de fazer deploy em produção.
Não apenas isso, ao passarmos o parâmetro watch como abaixo, garantimos que o TSX fique sempre escutando por alterações nos arquivos, recarregando a aplicação automaticamente toda vez que algo mudar, semelhante ao que você conseguiria com pacotes como Nodemon.
|
1 2 3 |
"dev": "npx tsx watch src/app.ts" |
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.




