Como criar classes em TypeScript

Node.js

Como criar classes em TypeScript

Luiz Duarte
Escrito por Luiz Duarte em 23/01/2024
Junte-se a mais de 34 mil devs

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

Há quem diga que JavaScript (e consequentemente TypeScript também) é melhor aproveitado no paradigma funcional do que no Orientado à Objetos, mas como um profissional que trabalhou mais de uma década com Java e C#, confesso que volta e meia me pego usando OO em aplicações Node.js e saber usar dos recursos OO disponibilizados a partir da especificação ES6 do JS é uma mão na roda. Não apenas isso, mas o uso de classes em TypeScript especificamente adiciona muitos poderes adicionais ao fornecido no JS tradicional, onde classes costumam ser vistas e tratadas apenas como syntax sugar.

No tutorial de hoje eu vou lhe ensinar o básico do uso de classes em TypeScript. Para que consiga aproveitar da melhor maneira possível este tutorial é interessante que você já saiba como usar classes em JS, algo que ensinei neste tutorial aqui. Além disso, não ensinarei o básico de TypeScript aqui, algo exaustivamente ensinado nesta série de tutoriais.

Sem mais delongas, vamos ao código!

Setup do Projeto

Crie uma pasta tutorial-classes na sua máquina e dentro dela inicialize um projeto Node.js.

Depois instale dentro da pasta as dependências que vamos precisar.

Na sequência, inicialize o TypeScript neste projeto.

Em seguida, configure o arquivo tsconfig.json para que procure os fontes na pasta src (que você deve criar) e que transpile os arquivos na pasta dist (que será criada automaticamente. Também tomei a liberdade de alterar a versão de ECMAScript para ES2020, que é a que mais tenho usado atualmente.

Dentro da pasta src você já pode deixar um arquivo index.ts preparado para nossos testes e no package.json pode ajustar o script de start para inicializar este arquivo e ficar escutando por alterações, com nodemon.

Pronto, agora temos tudo preparado para o tutorial.

Criando classes em TypeScript

Para quem não lembra ou ainda não aprendeu, uma classe é uma especificação, um tipo novo de objeto da sua aplicação. Por exemplo, uma classe Pessoa (inicie classes sempre com letra maiúscula e no singular) irá definir propriedades e funções comuns a pessoas da sua aplicação. Assim, quando for criar pessoas usando esta classe, elas sempre possuirão a mesma estrutura.

A primeira recomendação é que você use uma classe por arquivo TS, tranformando-o em um módulo TS que deverá ser importado onde se desejar usar essa classe. Esse arquivo deve ter o mesmo nome da classe, como abaixo, onde crio uma classe Customer, e deve ser a exportação default do módulo, por conveniência.

Dentro do escopo da classe podemos criar seus atributos (variáveis internas) e métodos (funções internas), que estarão presentes em todos os objetos inicializados a partir desta classe. Abaixo, algumas sugestões de exemplo e repare que já os deixei tipados.

Você vai notar que o VS Code, ao detectar que seu projeto é TypeScript e que você declarou a classe acima, vai reclamar. Isso porque quando criamos uma classe com atributos (também chamados de propriedades), devemos atribuir valores default à eles ou então criar um método construtor. Vou falar de construtores mais à frente, mas primeiro, apenas atribua valores default aos atributos, como abaixo:

Abaixo um exemplo de como um index.ts pode ser utilizado para “brincar” com nossa nova classe.

Para executar este arquivo, você pode usar o comando npm start (se ainda não o fez). Verá que ao imprimir o customer1, todos os dados default são apresentados, com exceção do name que alteramos para “Luiz”.

Agora se você não quiser ter valores default para os atributos, mas sim obrigar quem inicializar um novo cliente a preencher todos os campos (ou parte deles), o caminho é criar um construtor para esta classe. Um construtor é uma função especial que inicializa um objeto desta classe, usando argumentos e processamentos internos para definir as suas propriedades/atributos.

Esse construtor acima espera nome, e-mail e data e nascimento e os utiliza para definir as propriedades homônimas de todo cliente criado a partir dessa classe. Internamente, o construtor repassa esses valores para as propriedades do objeto, iniciadas com ‘this.’. Variáveis precedidas por ‘this.’ são atributos do objeto e seu uso é obrigatório em classes para não confundir com variáveis de outros escopos.

Note também que ele inicializa uma propriedade age de maneira automática e transparente, pegando o ano atual e comparando com o ano da data de nascimento. Embora este cálculo não seja 100% preciso ele serve como exemplo do tipo de processamento que pode ser realizado no construtor inclusive para validar e transformar dados passados como argumento.

O uso da palavra reservada constructor somente pode ser usada nessa função e ela é disparada automaticamente quando criamos um novo objeto Cliente usando a keyword new, como em outras linguagens orientadas a objeto (Java, C#, etc).

Se você voltar no trecho de código anterior, onde declarei o construtor, verá que cada um desses argumentos será colocado em uma propriedade interna do cliente e isso se torna evidente quando você imprime o objeto cliente1 no console no código acima.

Cada variável declarada como sendo um novo cliente tem o seu próprio conjunto de propriedades, mas a mesma estrutura básica, ficou claro?

Assim, se você declarar customer1, customer2, etc; cada um terá o seu nome, sua idade, etc. Independente um do outro, mas com o mesmo “esqueleto”.

Mas e os comportamentos?

Funções de classe em TypeScript

Toda classe é composta de atributos e métodos, também chamados de propriedades e funções. Essas funções, por uma questão de organização, devem ser sempre relativas à responsabilidade da classe em si, e geralmente manipulam ou utilizam as propriedades do objeto em questão. Assim, uma classe Customer terá funções que usam ou manipulam as propriedades do objeto customer em si.

Declarar uma função de classe (chamada de método em muitas linguagens OO) é feita dentro do escopo da mesma (abre e fecha-chaves mais externas). Não há necessidade da palavra function tradicionalmente usada, mas o restante segue a mesma lógica de functions tradicionais.

No exemplo acima descrevo que os objetos do tipo/classe Customer possuem duas funções: isAdult que retorna true/false com base na idade do objeto e outra chamada getFirstName que baseada no nome do objeto/cliente, retorna a primeira parte do mesmo (primeiro nome).

Para chamar estas funções você primeiro deve instanciar objetos do tipo Cliente e suas execuções devem produzir retornos conforme propriedades de cada objeto em particular.

No código acima, eu instancio dois clientes com dados diferentes e depois chamo a função isAdult pra ver quais deles são adultos ou não, com base na data de nascimento informada na sua criação.

O resultado você pode ver no seu console, mas basicamente o cliente de nome Luiz é adulto, enquanto que o cliente Pedro não é.

E por fim, note que você pode ter propriedades internas que, por sua vez, são do tipo de outra classe. Elas podem ser instanciadas com new dentro do próprio construtor, em uma variável antes ou até depois, conforme sua lógica necessitar.

Para este exemplo, considere a classe Address abaixo, declarada em outro arquivo na mesma pasta src:

Note que criei uma função toString() que monta uma String baseada nas propriedades do endereço. Na verdade já existe uma função toString automaticamente em todas classes JavaScript, mas estamos sobrescrevendo seu comportamento padrão através de declaração de outra função de mesmo nome. Isso é chamado de sobrescrita na orientação à objetos, uma das formas conhecidas de sobrecarga de método/função.

Curso FullStack

Agora altere a nossa classe Customer para que eles possuam uma propriedade address também:

Para testar o uso de objetos Address como propriedade do Customer, meu index.ts vai ficar assim:

O resultado é o endereço completo por extenso. Isso porque chamamos a função toString do objeto address. O uso de objetos dentro de outros objetos é o que chamamos de associação, sendo que podemos ter associações de agregação ou de composição. Bacana, não?

Herança em TypeScript

Para encerrar este artigo vamos falar agora de mais uma característica de Orientação à Objetos implementada no TypeScript: a Herança. Esse é um recurso que não existe em classes JS e foi uma grande adição do TS na minha opinião. Basicamente a Herança funciona assim: uma classe A, mais genérica, define propriedades e funções que podem ser herdadas por uma classe B (ou C, D, etc). Isso aumenta o reuso de código e consequentemente facilita a manutenção dos sistemas.

Experimente por exemplo criar um novo arquivo person.ts com uma classe Person dentro, representando uma pessoa genérica do sistema (vou usar como base aqui nosso customer original).

Agora vamos modificar nosso Customer para que ele herde as características de Person e apenas adicione o que for exclusivo de clientes, que neste exemplo (didático) será apenas o endereço. A herança de uma classe em relação à outra ocorre através da keyword extends, como abaixo.

No momento que eu digo que Customer extends Person, estou dizendo que todo objeto cliente, já nascerá também com todos os dados e funções de pessoa. O único detalhe que devo prestar atenção aqui é no constructor, que eu devo chamar na primeira linha dele a função super, que serve para chamar o constructor da superclasse (a classe da qual estou herdando). Essa é uma exigência e o TS não vai compilar se você não fizer.

Agora no nosso script de teste, não precisamos alterar absolutamente nada, mas repare que você pode chamar no objeto customer1 qualquer propriedade ou função que exista tanto em Customer quanto em Person, sem qualquer diferença. Na verdade, para o JS realmente não existe qualquer diferença, este é um recurso exclusivo do TS mesmo, que em tempo de compilação apenas copia os dados/funções da superclasse para as classes que herdam dela. A boa e velha syntax sugar.

Existem muitos outros conceitos relacionados à orientação à objetos presentes na linguagem TypeScript, mas por ora, os conceitos mais importantes ligados a classes em TS eu apresentei neste artigo.

Espero que tenham gostado!

Curso Node.js e MongoDB

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 *

2 Replies to “Como criar classes em TypeScript”

Janderson Weller

Ótima explicação, direto ao ponto.

Luiz Duarte

Fico feliz que tenha gostado Janderson!