Como usar TypeScript com Node.js - Parte 3

Node.js

Como usar TypeScript com Node.js - Parte 3

Luiz Duarte
Escrito por Luiz Duarte em 28/04/2026
Junte-se a mais de 34 mil devs

Entre para minha lista e receba conteúdos exclusivos e com prioridade

Avançando mais em nossos estudos de TypeScript, hoje vamos falar de mais alguns tópicos intermediário-avançados com esse superset, alguns até mesmo que são popularmente cobrados em entrevistas de emprego.

Se preferir, você pode assistir ao vídeo abaixo ao invés de ler.

#1 – Interfaces

Até agora estávamos usando a keyword type para definir novos tipos, não é mesmo? Mas essa não é a única keyword para isso no TypeScript. Além das óbvias classes com o uso de class, que veremos em mais detalhes em outro tutorial, nós temos também em TS as interfaces.

As interfaces, assim como normalmente visto na POO, é um recurso de definição de “contratos” entre objetos, ou seja, serve tanto para especificar os tipos dos dados como os comportamentos, ao menos em linhas gerais, dos mesmos. Pense nas interfaces como types tunados, motivo pelo qual é muito comum os times não usarem types e sim interfaces para tudo, já que elas permitem o mesmo que types mais uma série de coisas adicionais, bem aderentes ao paradigma OO.

Vamos a um exemplo:

Isso você já conseguia fazer com types, certo? Interfaces definem não apenas as propriedades, mas os comportamentos, como abaixo.

Isso quer dizer que todo objeto de Cliente deve ter também a implementação de uma função de salvar, que não espera parâmetro e não tem retorno (void). Se você tentar declarar um cliente sem essa função, terá erro de compilação. Uma declaração correta de cliente agora pode ser vista abaixo.

Eventualmente você pode ter campos que são opcionais, certo? Isso vale tanto para types quanto para interfaces. Para criar esse recurso você tem algumas opções:

  • usar um Union Type com undefined no campo;
  • usar um Uniont Type com null no campo (null é um valor e um tipo ao mesmo tempo);
  • tornar a propriedade opcional no tipo;

E esse último modo que quero mostrar abaixo, onde estou dizendo que dataNascimento é opcional com o uso do operador “?” na definição da interface.

Veja como eu pude declarar um cliente sem especificar a data de nascimento dele, algo que seria impossível na configuração anterior. Lembrando que, por padrão, propriedades não inicializadas ficam com o tipo e valor undefined em JavaScript, que é o que vai acontecer com dataNascimento neste caso.

#2 – String Types

Provavelmente você já conhece Template Literals, certo? É aquele recurso em que você cria strings entre crases e consegue injetar variáveis facilmente dentro dela. Mas você sabia que pode criar Template Literal Types com elas?

Digamos que o seu tipo de dado seja string, mas não qualquer string. Um endereço de carteira na blockchain por exemplo, tem regras específicas como começar a string com 0x. Veja abaixo um tipo que define isso.

Aqui estou dizendo que um novo tipo Address é uma string iniciada em 0x com outra string qualquer na sequência. Se no teste que faço a seguir você começar a string atribuída com qualquer coisa que não seja 0x, terá erro de compilação.

Outro truque muito popular usando TypeScript é definir quais os valores possíveis para um determinado campo a partir de Union Types com literais. Veja abaixo.

O type Role é uma string, mas não qualquer string: ele somente pode conter os valores admin ou user ou guest. Qualquer outro valor nele vai dar problema, será erro de compilação. O restante do código é apenas um exemplo de uso. Embora eu tenha colocado este recurso aqui por ser muito usado com strings, ele vale para qualquer tipo de dados, veja:

#3 – Generics

Generics é um recurso que permite a você trabalhar com qualquer tipo de dado, ao mesmo tempo que você ainda o mantém type safe. Mas como isso seria possível?

Lembra quando eu dei um exemplo de declaração de array, lá na segunda parte desta série? Foi algo mais ou menos assim:

Aqueles colchetes-angulares envolvendo o type number (<>) são a declaração de tipo genérico ou Generics e permitem que a gente passe qualquer tipo ali. Assim que você define o tipo genérico na declaração, a estrutura mais externa (Array) se torna especializada naquele tipo de dados. Ou seja, ao invés de ser um array de objetos quaisquer, ele se tornar um array apenas de números e a tipagem irá funcionar de acordo com esta nova identidade, ao invés da anterior que era genérica (daí o nome).

Mas novamente: como isso é possível?

Para você poder definir um tipo como genérico, você deve fazê-lo na sua definição. Pegamos como exemplo uma estrutura de dados genérica (e inútil, apenas didática) que vai esperar dados de um tipo que ainda não sabemos qual é. A definição genérica dela fica como segue:

Agora, quando declaramos um objeto com este tipo, temos que informar entre <> qual o tipo de dado que será usado com ela. Veja dois exemplos abaixo, um onde usamos o tipo com números e outro com strings.

Experimente colocar dados de outros tipos no array e verá que não é possível. Esse é um pouco do poder do recurso de Generics do TypeScript, mas vai muito além disso, como veremos a seguir.

Curso Node.js e MongoDB

#4 – Pick Type

Nós vimos recursos ligados a união e intersecção de tipos, certo? Isso permite-nos criar tipos maiores e mais complexos facilmente. Mas e quando queremos o contrário, ou seja, criar tipos menores e mais simples? Nestes casos podemos usar o tipo genérico Pick, para “pegar” apenas algumas características de um outro tipo, criando um novo “menor”.

Peguemos como exemplo esse tipo abaixo, cheio de campos de Cliente:

E se eu quiser apenas nome e email, por exemplo? Eu poderia criar um outro tipo só para isso (com campos repetidos) ou ainda criar dois tipos separando os pedaços de Cliente e definir Cliente como a intersecção deles (&). Enfim, tem várias formas de fazer. Mas quando você quer apenas pegar alguns campos, como em argumentos de função, sem criar toda uma definição nova, o Pick pode ajudar, veja.

Aqui eu usei o Pick para criar um novo tipo (sem nome), pegando somente os campos nome e email do tipo Cliente, enquanto descarto o resto. Note que este é um uso mais complexo de generics, com dois parâmetros, algo perfeitamente possível em TypeScript.

Além do Pick existem muitos outros Utility Types como Omit, Partial e Required. A lista completa está disponível na documentação oficial.

Curso Web23
Curso Web23

#5 – Lookup Types

Também chamados de Indexed Access Types, eles permitem que você defina o tipo de um dado como sendo o mesmo de uma propriedade de outro tipo. Exemplo:

Se eu quiser que uma função espere um nome de cliente, eu posso dizer que o tipo dela é string, certo? Mas se eu achar que no futuro este tipo de dado pode mudar em Cliente, eu posso criar um lookup type para meu parâmetro da seguinte forma.

Aqui, a função espera um nome por parâmetro, que é do mesmo tipo que a propriedade nome do tipo Cliente.

E dá pra ir ainda mais longe, usando Keyof Constraints com Generics. Veja esse exemplo, um tanto quanto exótico mas igualmente ilustrativo:

Aqui eu criei uma função getValue que espera um tipo T e uma informação K, que deve ser uma das chaves (props) de T. Repare como usei logo abaixo dizendo que T é Cliente e K é email. Se você tentar passar um campo que não exista em Cliente no parâmetro K do generics, vai dar erro.

Agora olhe para os parâmetros da função: o primeiro é um objeto do mesmo tipo T do generics, enquanto que o segundo é uma chave do tipo K, ou seja, do mesmo tipo da propriedade K no tipo T. Assim, conectando com o exemplo de uso, se T é Cliente e K é email, logo, o tipo de key é string, pois email também é string.

Interessante, não?

Curso FullStack

#5 – Type Packages

Se você tiver algum comando Node.js que o TypeScript não reconheça nativamente ou de qualquer outra biblioteca, você pode instalar a dependência dos types da mesma, mas somente em ambiente de desenvolvimento, visto que TS serve para codificarmos melhor, certo?

O parâmetro –save-dev salvará esta dependência somente no seu ambiente de desenvolvimento, faz o mesmo que o “-D” que usei em outra oportunidade.

Mas o que é esse @types/node afinal?

Todos os pacotes @types são extensões de pacotes já existentes no NPM (escritos original em JavaScript) para que eles suportem TypeScript, fornecendo, entre outras coisas, os tipos definidos para estes pacotes. Com @types/node, o TS irá conseguir entender sintaxe específica do Node.js.

E se quisermos oferecer suporte a TS para outra biblioteca, como o Express por exemplo, podemos instalar o @types dele, o que nos dará não apenas suporte aos seus tipos, mas a minha tão amada funcionalidade de code complete no VS Code.

Não esqueça de sempre instalar em ambiente de desenvolvimento, para não pesar em produção desnecessariamente.

O mais legal é que atualmente existe suporte com @types à maioria das dependências mais populares do NPM, tornando o uso com TS muito mais interessante e produtivo que no passado.

Agora para aprender a criar web APIs usando TypeScript, recomendo este tutorial aqui.

Outro tópico com TS que recomendo que você estudo é sobre classes (POO).

 

TAGS:

Olá, tudo bem?

O que você achou deste conteúdo? Conte nos comentários.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *