Oi, estudante! Boas-vindas a mais um curso de TypeScript na Alura. Quem vai nos guiar nessa jornada de aprendizado será a instrutora Mônica Hillman.
Audiodescrição: Mônica Hillman é uma mulher branca. Tem olhos castanhos e cabelos castanhos com mechas loiras e franja. Usa um óculos de grau com armação redonda e piercing no septo. Está com uma camiseta preta. Ao fundo, parede branca com iluminação degradê do azul ao rosa e estante com livros e outras decorações.
O projeto no qual vamos codificar será o ByteBank. Já vamos receber o HTML, CSS e um pouco do TypeScript pronto. Porém, vamos avançar um pouco mais para aplicar orientação a objeto nesse projeto.
Vamos aprender sobre:
Para você aprender sobre esses conteúdos, é importante ter feito alguns pré-requisitos antes de continuar.
É importante que você saiba um pouco de HTML e CSS para entender como foi feito o layout, além de conhecer JavaScript já que o TypeScript é baseado nessa linguagem.
Se você se interessou por esses termos e quer aprender a aplicar orientação a objetos em seus projetos, não deixe de se matricular. Te esperamos no próximo vídeo!
Agora, faremos parte da equipe de desenvolvimento do ByteBank, um banco virtual.
Na tela, temos a página inicial acessada pela pessoa usuária dentro do banco. É composta por um cabeçalho com o logotipo do ByteBank e o nome da pessoa usuária que está acessando, a Joana da Silva Oliveira.
Abaixo, temos três colunas. A primeira coluna mostra os serviços que o banco disponibiliza. A segunda tem opções para conferir o saldo da conta corrente e cadastrar novas interações com a conta, como transferências, depósitos, saques, etc. A terceira coluna tem o extrato, ou seja, o histórico de cada uma dessas ações que foram geradas na conta.
Para conseguir continuar o desenvolvimento desse projeto, precisamos explorar o código no VS Code. Na barra lateral, vamos entrar em "src > types > Conta.ts
".
Nesse arquivo, conseguimos conferir alguns dos elementos presentes na tela inicial, como o saldo, histórico de transações, função de debitar
, etc.
Como temos um código com várias funções, como debitar
e depositar
, podemos interpretar que usamos o paradigma de programação funcional, pois é todo baseado em funções.
Porém, não vamos ter somente a conta da Joana em um banco. Quanto mais contas existirem, melhor para a instituição.
Por isso, vamos aplicar o paradigma de orientação a objetos para auxiliar na escalabilidade da nossa aplicação.
Para começar a fazer isso, vamos renomear o arquivo Conta.ts
para Conta-antiga.ts
, pois não vamos mais usar esse código, mas ele pode ficar de referência para o novo.
Dentro da pasta "types", vamos clicar com o botão direito do mouse e selecionar a opção "New File" (ou "Ctrl + N") para criar um novo arquivo chamado Conta.ts
.
Nele, vamos fazer uma export class
chamada Conta
e abrir chaves. Dentro das chaves, vamos colocar um nome
do tipo string
. Isto é, nome: string
.
Após acrescentar um ponto e vírgula, colocamos na próxima linha o saldo
do tipo number
igual à JSON
em maiúsculo .parse()
.
Entre os parênteses, escrevemos localStorage.getItem()
, passando o saldo
entre aspas duplas. Após fechar os parênteses de getItem()
e parse()
, vamos colocar duas barras verticais (pipes) seguido de 0
. Essas barras significam OU
.
Conta.ts
:
export class Conta {
nome: string;
saldo: number = JSON.parse(localStorage.getItem("saldo")) || 0;
}
Em seguida, vamos escrever transacoes
sem cedilha e em minúsculas que vai ser do tipo Transacao
com "T" maiúsculo seguido de abre e fecha colchetes.
O VS Code aponta um erro. Para resolvê-lo, basta selecionar Transacao
com "T" maiúsculo e apertar "Ctrl + Espaço". Com isso, o VS Code sugere fazer o import desde ./Transacao
.
Após fazer essa importação, vamos continuar a escrever a linha de transações que vai ser igual à JSON.parse()
. Entre parênteses, digitamos localStorage.getItem()
, passando transacoes
entre as aspas duplas nos parênteses.
Depois do fechamento dos parênteses de getItem()
, vamos abrir novamente parênteses para colocar os parâmetros de uma arrow function. Ou seja, () => {}
. Entre os parênteses, especificamos key
como string
e value
como any
.
No corpo da arrow function, vamos colocar if
com a condição key === "data"
, entre parênteses. Entre as chaves do if
, vamos escrever return new Date()
, passando o value
entre os parênteses. No final, acrescentamos um ponto e vírgula.
Depois do if
, vamos dar um "Enter" e colocar um return value
.
Para finalizar, após o fechamento de parse()
, vamos colocar || []
que sinaliza "ou uma lista vazia".
import { Transacao } from "./Transacao";
export class Conta {
nome: string;
saldo: number = JSON.parse(localStorage.getItem("saldo")) || 0;
transacoes: Transacao[] = JSON.parse(localStorage.getItem("transacoes"), (key: string, value: any) => {
if (key === "data") {
return new Date(value);
}
return value;
}) || [];
}
Antes de continuar a escrever mais código, vamos entender tudo o que colocamos nesse arquivo.
Basicamente, queremos um nome
que vai ser do tipo string
. Por exemplo, uma palavra ou uma série de caracteres.
Depois, definimos um saldo
que vai receber algo do tipo número. Esse valor é retornado do localStorage
(armazenamento local), ou seja, do que estiver salvo no nosso navegador. Se não tiver nada, vai ser retornado um 0
.
Também declaramos algo que vai chamar transacoes
que vai do tipo Transacao
que é um tipo customizado que importamos de ./Transacao
. Com isso, vamos pegar o valor do localStorage
e transformá-lo em uma maneira que seja possível mostrar na tela futuramente.
Não temos nada de novo nesse código. Se você conferir o conteúdo da Conta-antiga.ts
, temos o mesmo código da linha 5 a linha 12.
Vamos continuar a escrever em Conta.ts
. Ainda na classe Conta
, após transacoes
, vamos colocar um constructor()
. Entre parênteses, digitamos nome: string
.
No corpo do construtor entre as chaves, vamos retornar um this.nome
igual à nome
.
export class Conta {
// código omitido…
constructor(nome: string) {
this.nome = nome;
}
}
Já que revimos trechos do código que já tinham sido feitos, vamos entender o que colocamos de novo nesse código.
Criamos uma classe chamada Conta
. Uma classe são características que vão se tornar o objeto no futuro. Portanto, declaramos que cada conta vai ter nome, saldo e transações que podem ser cadastradas.
Quando construímos esse constructor
(construtor), declaramos o atributo nome
dentro dele. Assim, cada vez que criamos uma conta, a única informação obrigatória será o nome.
Os outros atributos vão ser de acordo com funcionalidades que vamos acrescentar no futuro para implementar nesse tipo de conta.
Podemos testar isso ao criar uma conta. Após a classe, vamos criar uma const
chamada conta
em minúsculo que vai ser igual à new Conta()
com "C" maiúsculo, pois estamos instanciando uma classe.
Entre os parênteses, vamos colocar Joana da Silva Olveira
entre aspas duplas que é a única usuária do ByteBank. Ao final, colocamos ponto e vírgula.
Na próxima linha, vamos fazer um export default
de conta
em minúsculo.
const conta = new Conta("Joana da Silva Olveira");
export default conta;
Para verificar se a conta foi realmente criada, precisamos procurar em qual arquivo é programado para aparecer os elementos na tela.
Dentro da estrutura de pastas desse projeto em específico, precisamos ir em "src > components > extrato-component.ts
". Esse é o primeiro local em que conseguimos encontrar.
Na linha 1, estamos importando Conta
desde Conta-antiga.js
, porque renomeamos o arquivo antigo e esse novo nome foi puxado para todas as importações desse caminho.
Vamos remover esse Conta-antiga.js
e deixar somente Conta.js
na importação. Após salvar, o VS Code já aponta um erro na linha 10 em Conta.getGruposTransacoes()
, porque essa propriedade não existe no tipo Conta
.
Realmente, ela ainda não existe.
extrato-component.ts
:
import Conta from "../types/Conta.js";
import { FormatoData } from "../types/FormatoData.js";
import { GrupoTransacao } from "../types/GrupoTransacao.js";
import { formatarMoeda, formatarData } from "../utils/formatters.js";
const elementoRegistroTransacoesExtrato: HTMLElement = document.querySelector(".extrato .registro-transacoes");
renderizarExtrato();
function renderizarExtrato(): void {
const gruposTransacoes: GrupoTransacao[] = Conta.getGruposTransacoes();
elementoRegistroTransacoesExtrato.innerHTML = "";
let htmlRegistroTransacoes: string = "";
// código omitido…
No Conta.ts
, o único que fizemos foi declarar atributos padrões que o objeto do tipo Conta
precisa ter, como nome
, saldo
e transacoes
. Depois, apenas criamos uma nova conta com o nome da Joana.
Para criar essas ações que a pessoa usuária pode fazer com a conta e retornar esse histórico de transações, vamos precisar fazer algo semelhante a funções, mas com orientação a objeto. No próximo vídeo, vamos descobrir como fazer isso. Até lá!
Quando começamos a construir a classe, comentamos que em orientação a objetos existe uma maneira de construir as interações da pessoa usuária com o nosso objeto.
Para fazer isso, vamos começar com o getGruposTransacoes()
que deu erro em extrato-component.ts
. Devemos abrir o arquivo Conta-antiga.ts
e verificar por volta da linha 44, onde começa a função getGruposTransacoes()
.
Temos um código de várias linhas. Vamos copiar com "Ctrl + C" da linha 44 a 63, ou seja, toda a função:
Conta-antiga.ts
:
getGruposTransacoes(): GrupoTransacao[] {
const gruposTransacoes: GrupoTransacao[] = [];
const listaTransacoes: Transacao[] = structuredClone(transacoes);
const transacoesOrdenadas: Transacao[] = listaTransacoes.sort((t1, t2) => t2.data.getTime() - t1.data.getTime());
let labelAtualGrupoTransacao: string = "";
for (let transacao of transacoesOrdenadas) {
let labelGrupoTransacao: string = transacao.data.toLocaleDateString("pt-br", { month: "long", year: "numeric" });
if (labelAtualGrupoTransacao !== labelGrupoTransacao) {
labelAtualGrupoTransacao = labelGrupoTransacao;
gruposTransacoes.push({
label: labelGrupoTransacao,
transacoes: []
});
}
gruposTransacoes.at(-1).transacoes.push(transacao);
}
return gruposTransacoes;
},
Em seguida, vamos até o novo arquivo que estamos alterando, o Conta.ts
. Depois do constructor
, mas antes do fechamento da classe Conta
, vamos dar um "Enter" e colar com "Ctrl + V" esse trecho que na programação funcional chamava-se de função.
Antes de explicar o que getGruposTransacoes()
é em nosso novo paradigma, vamos arrumar os erros.
Primeiro, precisamos importar o GrupoTransacao
na linha 17 com "Ctrl + Espaço". O próximo erro aparece na linha 20, em structuredClone()
. Nesse trecho, vamos precisar adicionar um this.transacoes
entre os parênteses.
Conta.ts
:
import { GrupoTransacao } from "./GrupoTransacao";
export class Conta {
// código omitido…
getGruposTransacoes(): GrupoTransacao[] {
const gruposTransacoes: GrupoTransacao[] = [];
const listaTransacoes: Transacao[] = structuredClone(this.transacoes);
const transacoesOrdenadas: Transacao[] = listaTransacoes.sort((t1, t2) => t2.data.getTime() - t1.data.getTime());
let labelAtualGrupoTransacao: string = "";
for (let transacao of transacoesOrdenadas) {
let labelGrupoTransacao: string = transacao.data.toLocaleDateString("pt-br", { month: "long", year: "numeric" });
if (labelAtualGrupoTransacao !== labelGrupoTransacao) {
labelAtualGrupoTransacao = labelGrupoTransacao;
gruposTransacoes.push({
label: labelGrupoTransacao,
transacoes: []
});
}
gruposTransacoes.at(-1).transacoes.push(transacao);
}
return gruposTransacoes;
}
}
const conta = new Conta("Joana da Silva Olveira");
export default conta;
Agora que estamos sem erros aparentes, podemos entender o que estamos fazendo. Quando colocamos essas interações e códigos do que conseguimos fazer com esse objeto, estamos colocando métodos - e não funções como acontece na programação funcional.
Visualmente, ambos se parecem. Pois, vamos colocar o nome do método seguido de parênteses e, em TypeScript, precisamos determinar o tipo. O que temos de diferente?
No structuredClone()
, não podemos declarar transacoes
diretamente. Precisamos colocar this.transacoes
, porque quando chamamos esse método getGruposTransacoes()
, estamos nos referindo aquele objeto em específico.
Por exemplo, criamos a conta da Joana ao fazer um new Conta()
e passar o nome dessa pessoa. Quando quisermos chamar esse getGruposTransacoes()
para visualizá-las, vamos chamar Conta.getGruposTransacoes()
que vai direto para a conta da Joana, a qual foi criada com o nome de conta.
São poucas mudanças, mas que fazem uma diferença para ajudar na escalabilidade para ter mais tipos de contas e pessoas usuárias em nosso projeto.
Agora, podemos começar a adicionar outros métodos do Conta-antiga.ts
, como o getSaldo()
. Após o último método na classe Conta()
, basta escrever getSaldo()
. Entre chaves, colocamos return this.saldo
. Esse this
é a diferença para o funcional.
Depois, escrevemos o método getDataAcesso()
e declaramos o tipo Date
. Entre chaves, colocamos o return new Date()
, porque não queremos uma informação do nosso objeto, mas uma informação geral do JavaScript.
Também podemos copiar todo o trecho do registrarTransacao()
da Conta-antiga.ts
. No Conta.ts
, vamos colar após o getDataAcesso()
.
Com isso, surgem outros erros. Precisamos fazer um import de TipoTransacao
, usando o "Ctrl + Espaço".
Em seguida, temos um erro em depositar()
, pois o VS Code não encontra o depositar()
em nosso código. Isso porque está tentando chamar um método dentro de um método. Esse método depositar()
ainda não foi construído.
Em Conta-antiga.ts
, vamos procurar onde está esse depositar()
. Perceba que estão declarados visualmente como function debitar()
e function depositar()
, diferente das outras funções que havíamos construído.
Podemos, simplesmente, copiar de debitar()
até a última chave, sem o function
. Após colar em Conta.ts
, faremos o mesmo para depositar()
.
Agora, vamos arrumar os erros em nossa tela. Em registrarTransacao()
, podemos substituir o depositar()
para this.depositar()
e o debitar()
para this.debitar()
, pois nos referimos a esses métodos dentro do objeto da Conta
. Também, vamos colocar o this
em transacoes
nas linhas 60 e 62.
No método debitar()
que copiamos, vamos acrescentar o this
em frente ao saldo
nas linhas 69, 73 e 74. No método depositar()
, ainda precisamos adicionar mais this
em frente ao saldo
nas linhas 82 e 83.
Parece repetitivo, mas é porque estamos construindo uma migração de um projeto que já existia com programação funcional para orientação a objeto - não estamos construindo do zero.
E isso acontece, pois a tecnologia evolui cada vez mais rápido e, com isso, pegamos projetos legados que precisamos dar manutenção.
import { TipoTransacao } from "./TipoTransacao";
export class Conta {
// código omitido…
getSaldo() {
return this.saldo;
}
getDataAcesso(): Date {
return new Date();
}
registrarTransacao(novaTransacao: Transacao): void {
if (novaTransacao.tipoTransacao == TipoTransacao.DEPOSITO) {
this.depositar(novaTransacao.valor);
}
else if (novaTransacao.tipoTransacao == TipoTransacao.TRANSFERENCIA || novaTransacao.tipoTransacao == TipoTransacao.PAGAMENTO_BOLETO) {
this.debitar(novaTransacao.valor);
novaTransacao.valor *= -1;
}
else {
throw new Error("Tipo de Transação é inválido!");
}
this.transacoes.push(novaTransacao);
console.log(this.getGruposTransacoes());
localStorage.setItem("transacoes", JSON.stringify(this.transacoes));
}
debitar(valor: number): void {
if (valor <= 0) {
throw new Error("O valor a ser debitado deve ser maior que zero!");
}
if (valor > this.saldo) {
throw new Error("Saldo insuficiente!");
}
this.saldo -= valor;
localStorage.setItem("saldo", this.saldo.toString());
}
depositar(valor: number): void {
if (valor <= 0) {
throw new Error("O valor a ser depositado deve ser maior que zero!");
}
this.saldo += valor;
localStorage.setItem("saldo", this.saldo.toString());
}
}
Com isso, conseguimos colocar todos os tipos de métodos que já estavam na Conta-antiga.ts
para a nova Conta.ts
.
Podemos vamos voltar em extrato-component.ts
. Não tem mais erros acontecendo nesse arquivo. Agora, vamos verificar o nova-transacao-component.ts
ainda na pasta "src > components".
Na linha 4, ainda importamos a Conta-antiga
. Para arrumar, basta substituir para from "../types/Conta.js"
. Após fazer essa mudança, não surge nenhum erro porque já construímos todos os métodos que são usados para mostrar na tela.
No arquivo saldo-component.ts
, precisamos modificar a mesma importação. Na linha 3, vamos substituir Conta-antiga
para a nova Conta
.
nova-transaco-component.ts
esaldo-component.ts
:
import Conta from "../types/Conta.js";
Dessa forma, temos a nossa classe Conta
com todas as funcionalidades necessárias. O método getGruposTransacoes()
pega todos os tipos de transações que tem no local storage e faz a ordenação por data para ter as mais recentes acima.
No método getSaldo()
, vai ser retornado o saldo da Joana, pois é a pessoa usuária que criou a conta. Também temos o getDataAcesso()
para saber quando a pessoa usuária acessou a sua conta.
Além disso, o registrarTransacao()
salva no local storage o tipo da transação, como depósito ou transferência. A depender do tipo, são chamados métodos diferentes.
Por exemplo, se recebemos uma transação do tipo depósito, vai ser chamado outro método chamado depositar()
. Se recebemos uma transação do tipo transferência, vai ser chamado o método debitar()
para descontar da conta.
Essa lógica já está nas próximas linhas, no método debitar()
, o qual ocorre na transferência. Primeiro, ele vai verificar se o valor a debitar não é igual ou menor que zero e se a pessoa tem saldo para retirar esse dinheiro. Depois, ele faz essa transação, retirando o valor do saldo total.
Em seguida, temos o método depositar()
que faz o contrário. Ele insere uma quantia de dinheiro em nosso saldo.
Com isso, aprendemos como implementar funcionalidades em nosso código feito com orientação a objeto.
Existem mais diferenças entre programação funcional e programação orientada a objetos do que somente o que fizemos. No próximo vídeo, vamos entender isso mais a fundo na parte teórica. Vamos conhecer as vantagens e desvantagens de cada um. Até lá!
O curso TypeScript: aplicando orientação a objetos no Front-end possui 59 minutos de vídeos, em um total de 40 atividades. Gostou? Conheça nossos outros cursos de JavaScript em Front-end, ou leia nossos artigos de Front-end.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.