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:
Mais de 1500 cursos completamente atualizados, com novos lançamentos todas as semanas, emProgramação, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
Desafios temáticos para você turbinar seu portfólio. Você aprende na prática, com exercícios e projetos que simulam o dia a dia profissional.
Webséries exclusivas com discussões avançadas sobre arquitetura de sistemas com profissionais de grandes corporações e startups.
Emitimos certificados para atestar que você finalizou nossos cursos e formações.
Mais de 1500 cursos completamente atualizados, com novos lançamentos todas as semanas, emProgramação, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
Desafios temáticos para você turbinar seu portfólio. Você aprende na prática, com exercícios e projetos que simulam o dia a dia profissional.
Webséries exclusivas com discussões avançadas sobre arquitetura de sistemas com profissionais de grandes corporações e startups.
Emitimos certificados para atestar que você finalizou nossos cursos e formações.
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com Luri até 100 mensagens por semana.
Estude a língua inglesa com um curso 100% focado em tecnologia e expanda seus horizontes profissionais.
Acesso completo
durante 1 ano
Estude 24h/dia
onde e quando quiser
Novos cursos
todas as semanas