Prévia do material em texto
SumárioSumário
ISBNISBN
Sobre o livroSobre o livro
1. Introdução ao TypeScript1. Introdução ao TypeScript
2. Conhecendo os types2. Conhecendo os types
3. Estruturas de controle e 3. Estruturas de controle e repetiçãorepetição
4. POO (Programação Orientada a Objetos)4. POO (Programação Orientada a Objetos)
5. Interfaces5. Interfaces
6. Generics6. Generics
7. Decorator 7. Decorator
8. Modules e namespaces8. Modules e namespaces
9. Visual Studio Code9. Visual Studio Code
10. Do10. Docker: Configurando acker: Configurando ambiente de banco de dadosmbiente de banco de dados
11. Cri11. Criando API TypeScript,ando API TypeScript, Node.js, MongoDB e Docker Node.js, MongoDB e Docker
12. Cr 12. Cr iando novas modelsiando novas models
13. Inj13. Injeção de Dependênciaeção de Dependência
14. Do14. Documentando o pcumentando o projetorojeto
15. Co15. Conclusãonclusão
ISBNISBN
Impresso: 978-65-86110-77-7Impresso: 978-65-86110-77-7
Digital: 978-65-86110-78-4Digital: 978-65-86110-78-4
Caso você deseje submeter alguma errata Caso você deseje submeter alguma errata ou sugestão, acesseou sugestão, acesse
http://erratas.casadocodigo.com.br http://erratas.casadocodigo.com.br ..
Sobre o livroSobre o livro
Este livro é destinado a Este livro é destinado a profissionais que têm interesse em aprender profissionais que têm interesse em aprender
a trabalhar com TypeScript por meio de exemplos práticos e a trabalhar com TypeScript por meio de exemplos práticos e reais.reais.
A ideia central A ideia central é passar tudo o qé passar tudo o que eu aprendi ue eu aprendi desenvolvendodesenvolvendoprojetos com TypeScriprojetos com TypeScript nos últimos anos pt nos últimos anos nos meus trabalhos comonos meus trabalhos como
freelancer e na TV Bandeirantes. Vamos iniciar abordando conceitosfreelancer e na TV Bandeirantes. Vamos iniciar abordando conceitos
básicos, como os básicos, como os tipos suportados pelo Typtipos suportados pelo TypeScript até a construçãoeScript até a construção
de uma API que retorna os dados de uma base de dados MongoDB,de uma API que retorna os dados de uma base de dados MongoDB,
que será configurada em um contêiner Docker.que será configurada em um contêiner Docker.
Como pré-requisito, você precisa conhecer lógica de programação,Como pré-requisito, você precisa conhecer lógica de programação,
ter um conhecimento básico de JavaScript e muita vontade deter um conhecimento básico de JavaScript e muita vontade de
aprender algo novo e disposição para replicar cada aprender algo novo e disposição para replicar cada um dosum dos
exemplos aqui demonstrados.exemplos aqui demonstrados.
Ao final deste Ao final deste livro, você terá desenlivro, você terá desenvolvido uma solução volvido uma solução completacompleta
com TypeScricom TypeScript, passando por todas as pt, passando por todas as etapas que eu utilizei paraetapas que eu utilizei para
desenvolver uma API para o programa MasterChef em uma de suasdesenvolver uma API para o programa MasterChef em uma de suas
edições anteriores.edições anteriores.
Para isso, nós utilizaremos as Para isso, nós utilizaremos as seguintes ferramentas e tecnologias:seguintes ferramentas e tecnologias:
Visual Studio CodeVisual Studio Code
Node.jsNode.js
TypeScript na versão 4.2.3TypeScript na versão 4.2.3Docker para ambiente de desenvolvimentoDocker para ambiente de desenvolvimento
MongoDB como base de dadosMongoDB como base de dados
AgradecimentosAgradecimentos
Primeiramente, gostaria de agradecer a Deus por tudo o Primeiramente, gostaria de agradecer a Deus por tudo o que ele temque ele tem
feito na minha vida! feito na minha vida! À minha mãe Elenice pelo amorÀ minha mãe Elenice pelo amor, força, incentivo, força, incentivo
e por todo o apoio que me deu desde o meu primeiro dia de vida. Àe por todo o apoio que me deu desde o meu primeiro dia de vida. À
minha irmã Thamiris por estar sempre ao meu lado, nos momentosminha irmã Thamiris por estar sempre ao meu lado, nos momentos
bons e ruins.bons e ruins.
À minha esposa Juli À minha esposa Juliana pelo apoana pelo apoio e motivação nesses úio e motivação nesses últimosltimos
meses em que me dediquei a escrever este livro e meses em que me dediquei a escrever este livro e pela parceria depela parceria de
vida.vida.
À minha filha A À minha filha Agnes que, mesmo sem sabgnes que, mesmo sem saber ler ou prograer ler ou programarmar, fez, fez
algumas revisões comigo algumas revisões comigo brincando e escutando galinha pintadinha.brincando e escutando galinha pintadinha.
Ao meu primo Rafa Ao meu primo Rafael Izidoro, que el Izidoro, que me proporcionou me proporcionou o melhor o melhor
investimento da minha vida: uma caixa de cerveja por três meses deinvestimento da minha vida: uma caixa de cerveja por três meses de
aula de programação. Sem esse investimento eu aula de programação. Sem esse investimento eu não teria entradonão teria entrado
para a área de para a área de tecnologia e não estaria escrevendo este livro hoje.tecnologia e não estaria escrevendo este livro hoje.
Ao Gabriel Schad Ao Gabriel Schade Cardoso por ter e Cardoso por ter acreditado no lacreditado no livro e ter feito aivro e ter feito a
ponte para que ele se ponte para que ele se tornasse realidade.tornasse realidade.
Por fim, Vivian Matsui, muito obrigado pela parceria, paciência ePor fim, Vivian Matsui, muito obrigado pela parceria, paciência e
dedicação nos últimos meses em que finalizamos este livro juntos ededicação nos últimos meses em que finalizamos este livro juntos e
agradeço à Casa do Código pela agradeço à Casa do Código pela oportunidade de realizar esseoportunidade de realizar esse
sonho de escrever um livro com vocês.sonho de escrever um livro com vocês.
Sobre o autor Sobre o autor
Figura 0.1: Thiago Silva AdrianoFigura 0.1: Thiago Silva Adriano
Sou Microsoft (MVP) e Sou Microsoft (MVP) e atualmente trabalho como líder técnico naatualmente trabalho como líder técnico na
empresa TV Bandeirantes. Nestes últimos anos, foquei nasempresa TV Bandeirantes. Nestes últimos anos, foquei nas
tecnologias criadas pela Microsoft, mas estou sempre tecnologias criadas pela Microsoft, mas estou sempre antenado comantenado com
as novas tendências que estão as novas tendências que estão surgindo no mercado. Sou umasurgindo no mercado. Sou uma
pessoa apaixonada pelo que faz e pessoa apaixonada pelo que faz e tem a sua tem a sua profissão como hobbyprofissão como hobby..
Blog:Blog: https://programadriano.medium.com/https://programadriano.medium.com/
GitHub:GitHub: https://github.com/programadrianohttps://github.com/programadriano
Podcast:Podcast: https://devshow.com.br https://devshow.com.br , onde eu e alguns amigos, onde eu e alguns amigos
falamos sobre vários assuntos em alta na comunidade dev.falamos sobre vários assuntos em alta na comunidade dev.
Participo das comunidades:Participo das comunidades:
.NET SP: a maior comunidade de .NET:.NET SP: a maior comunidade de .NET:
https://wwwhttps://www.meetup.com/pt-BR/do.meetup.com/pt-BR/dotnet-Sao-Paulotnet-Sao-Paulo//
SampaDevs: comunidade criada para compartilhamento deSampaDevs: comunidade criada para compartilhamento de
conhecimento sobre todas as tecnologias:conhecimento sobre todas as tecnologias:
https://wwwhttps://www.meetup.com/pt-BR/Sa.meetup.com/pt-BR/SampaDevs/mpaDevs/
AprendendoJS: co AprendendoJS: comunidade nova criamunidade nova criada parada para meetupsmeetups sobre sobre
JavaScript:JavaScript: https://wwwhttps://www.meetup.com/pt-BR/.meetup.com/pt-BR/learning-nodelearning-nodejs/js/
C 1C 1
Introdução ao TypeScriptIntrodução ao TypeScript
O TypeScripO TypeScript é um t é um pré-processador (pré-processador (superset superset ) de códigos) de códigos
JavaScriptJavaScript open sourceopen source desenvolvido e mantido pela Microsoft. Ele desenvolvido e mantido pela Microsoft. Ele
foi desenvolvido pelo tifoi desenvolvido pelo time do Anders Hme do Anders H ejlsberg, arquitetejlsberg, arquitetolíder doo líder do
time Typetime TypeScript e desenvolvedor do C#Script e desenvolvedor do C#, Delphi e do , Delphi e do TuTurbo Pascal. Arbo Pascal. A
sua primeira versão, a 0.8, foi lançada em 1 de outubro de 2012.sua primeira versão, a 0.8, foi lançada em 1 de outubro de 2012.
Ele foi projetado para auxiliar no desenvolvimento de códigosEle foi projetado para auxiliar no desenvolvimento de códigos
simples até os mais complexos, utilizando os princípios desimples até os mais complexos, utilizando os princípios de
Orientação a Objetos, como classes, Orientação a Objetos, como classes, tipagens, interfaces, Genericstipagens, interfaces, Generics
etc.etc.
Aqui chegamos à p Aqui chegamos à primeira dúvida de rimeira dúvida de todo desenvolvedtodo desenvolvedor que estáor que está
iniciando com TypeScript: por que tipar o iniciando com TypeScript: por que tipar o JavaScript?JavaScript?
Essa, na realidade, é uma das vantagens de se trabalhar com esseEssa, na realidade, é uma das vantagens de se trabalhar com esse
pré-processador. A utilização de tipagem ajuda no momento depré-processador. A utilização de tipagem ajuda no momento de
depuração (debug) do nosso código, prevenindo alguns possíveisdepuração (debug) do nosso código, prevenindo alguns possíveis
bugs ainda em tempo de bugs ainda em tempo de desenvolvimento.desenvolvimento.
Mas como ele funciona?Mas como ele funciona?
Como os Como os navegadores não interpretam código TypeScriptnavegadores não interpretam código TypeScript, nós, nós
precisamos transpilar o nosso código para uma das versõesprecisamos transpilar o nosso código para uma das versões
ECMAScript.ECMAScript.
A figura a segu A figura a seguir demonstra o seu pir demonstra o seu processo de compilaçãorocesso de compilação
((transpiler transpiler ). O arquivo). O arquivo .ts.ts é o é o código desenvolvido em TypeScript, acódigo desenvolvido em TypeScript, a
engrenagem é o compilador, e o arquivoengrenagem é o compilador, e o arquivo .js.js é o código finalé o código final
JavaScript.JavaScript.
Figura 1.1: Processo de compilação.Figura 1.1: Processo de compilação.
1.1 Instalação1.1 Instalação
Para instalar o TypeScript, é necessário ter o Node.js e Para instalar o TypeScript, é necessário ter o Node.js e o seuo seu
gerenciador de pacotes, o gerenciador de pacotes, o NPM, instalados no NPM, instalados no seu computadorseu computador..
Caso você ainda não tenha, basta acessar o linkCaso você ainda não tenha, basta acessar o link
https://nodejs.org/en/https://nodejs.org/en/ e baixar um executável de acordo com o e baixar um executável de acordo com o seuseu
sistema operacional.sistema operacional.
Para verificar se ele foi instalado corretamente ou qual é a versãoPara verificar se ele foi instalado corretamente ou qual é a versão
que você tem instalada, basta abrir que você tem instalada, basta abrir um terminal no seu um terminal no seu computador computador
e digitar o comando:e digitar o comando: node -v && npm -vnode -v && npm -v ..
Figura 1.2: Versão do Node.js e NPM.Figura 1.2: Versão do Node.js e NPM.
Ao utilizar o Ao utilizar o npmnpm seguido deseguido de installinstall , estamos passando para o, estamos passando para o
gerenciador que ele deve instalar o pacote informado depois dagerenciador que ele deve instalar o pacote informado depois da
instruçãoinstrução installinstall ..
Um ponto importante para destacar neste momento é Um ponto importante para destacar neste momento é que, quandoque, quando
trabalhamos com pacotestrabalhamos com pacotes npmnpm , é comum ter alguns deles instalados, é comum ter alguns deles instalados
em modo global, o que nos permite executar esse pacote deem modo global, o que nos permite executar esse pacote de
qualquer lugar do nosso computador.qualquer lugar do nosso computador.
Para ter um pacotePara ter um pacote npmnpm instalado em modo global, basta adicionar oinstalado em modo global, basta adicionar o
-g-g antes do nome do pacote.antes do nome do pacote.
Vamos instalar o TypeScript em modo global. Para isso, digite oVamos instalar o TypeScript em modo global. Para isso, digite o
comando:comando: npm install -g npm install -g typescripttypescript no seu terminal.no seu terminal.
Uma vez Uma vez instalado, o compilador do instalado, o compilador do TTypeScript estará disponívelypeScript estará disponível
executando o comandoexecutando o comando tsctsc . Para verificar a versão instalada, basta. Para verificar a versão instalada, basta
digitar o comandodigitar o comando tsc -vtsc -v dentro de um terminal.dentro de um terminal.
Figura 1.3: Figura 1.3: Versão do TypeScript.Versão do TypeScript.
1.2 Executando manualmente o TypeScript1.2 Executando manualmente o TypeScript
Com o TypeScript instalado em modo global, crie um novo Com o TypeScript instalado em modo global, crie um novo diretóriodiretório
no seu computador para organizar os no seu computador para organizar os exemplos desta seção. Dentroexemplos desta seção. Dentro
desse diretório, crie um arquivo chamado:desse diretório, crie um arquivo chamado: meuprimeiro.tsmeuprimeiro.ts ..
Para os próximos passos será necessária a utilização de um editor Para os próximos passos será necessária a utilização de um editor
de textos. Vou utilizar o Visual Studio Code pela sua integração comde textos. Vou utilizar o Visual Studio Code pela sua integração com
o TypeSo TypeScript, mas todos os cript, mas todos os exemplos aqui demonstradosexemplos aqui demonstrados
funcionarão em qualquer editor de textos.funcionarão em qualquer editor de textos.
Para instalar o Visual Studio Code, basta baixar um Para instalar o Visual Studio Code, basta baixar um executávelexecutável
correspondente ao seu SO (sistema operacional) e, em correspondente ao seu SO (sistema operacional) e, em seguida,seguida,
seguir os respectivos passos seguir os respectivos passos de instalação padrão. Segue link parade instalação padrão. Segue link paradownload:download: https://code.visualstudio.com/downloadhttps://code.visualstudio.com/download
Com o seu editor de textos aberto, abra o arquivoCom o seu editor de textos aberto, abra o arquivo meuprimeiro.tsmeuprimeiro.ts
nele e em seguida atualize-o com nele e em seguida atualize-o com o seguinte trecho de código:o seguinte trecho de código:
constconst a : a : stringstring = = 'Hello World''Hello World';;
consoleconsole.log(a);.log(a);
Abra um terminal Abra um terminal no seu computadorno seu computador, navegue até o , navegue até o diretório emdiretório em
que você criou o arquivoque você criou o arquivo meuprimeiro.tsmeuprimeiro.ts e execute o comandoe execute o comando tsctsc
meuprimeiro.tsmeuprimeiro.ts ..
Note que será criado um Note que será criado um novo arquivo chamadonovo arquivo chamado meuprimeiro.jsmeuprimeiro.js nana
raiz de sua pasta.raiz de sua pasta.
Agora, para vali Agora, para validar o passo anterdar o passo anteriorior, execute o comando, execute o comando nodenode
meuprimeiro.jsmeuprimeiro.js no seu terminal. Caso tudo esteja OK, você deveno seu terminal. Caso tudo esteja OK, você deve
receber a seguinte mensagem no seu receber a seguinte mensagem no seu terminal:terminal: Hello WorldHello World ..
1.3 Entendendo o 1.3 Entendendo o compilador do compilador do TTypeScriptypeScript
O compilador do O compilador do TTypeScript é altamente configurável. Ele ypeScript é altamente configurável. Ele nosnos
permite definir o local onde estão os permite definir o local onde estão os arquivosarquivos .ts.ts dentro do nossodentro do nosso
projeto, o diretório de destino dos arquivosprojeto, o diretório de destino dos arquivos transpiladostranspilados, a versão, a versão
ECMAScript que será utilizada, o nível ECMAScript que será utilizada, o nível de restrição do verificador dede restrição do verificador de
tipos e até se o compilador deve permitir arquivos JavaScript.tipos e até se o compilador deve permitir arquivos JavaScript.
Cada uma das opções de configuração pode serpassada para umCada uma das opções de configuração pode ser passada para um
arquivo chamadoarquivo chamado tsconfig.jsontsconfig.json . Para quem não conhece, esse é o. Para quem não conhece, esse é o
principal arquivo de principal arquivo de configuração do TypeScript.configuração do TypeScript.
Para criar esse arquivo dentro de um novo projeto, basta executar oPara criar esse arquivo dentro de um novo projeto, basta executar o
comandocomando tsc --inittsc --init . Isso vai criar um . Isso vai criar um arquivo na raiz do seu projetoarquivo na raiz do seu projeto
com algumas configurações padrões, comocom algumas configurações padrões, como targettarget ,, modulemodule entreentre
outras. A seguir você tem uma imagem demonstrando esse arquivooutras. A seguir você tem uma imagem demonstrando esse arquivo
com as configuraçõescom as configurações default default dele. dele.
Figura 1.4: Figura 1.4: Versão do TypeScript.Versão do TypeScript.
Para que você possa Para que você possa entender melhor como funciona o compiladorentender melhor como funciona o compilador,,
vamos criar vamos criar um novo arquivo um novo arquivo TTypeScript.ypeScript.
No mesmo diretório que você criou o seu arquivo de configuraçõesNo mesmo diretório que você criou o seu arquivo de configurações
no passo anteriorno passo anterior, crie um , crie um arquivo com a extensãoarquivo com a extensão .ts.ts . Para esse. Para esse
exemplo, eu criarei um arquivo chamadoexemplo, eu criarei um arquivo chamado index.tsindex.ts , mas você pode, mas você pode
escolher um nome de escolher um nome de sua preferência.sua preferência.
Um ponto importante que devemos sempre lembrar Um ponto importante que devemos sempre lembrar no momento deno momento de
criação de novos arquivos, é respeitar a convenção que oscriação de novos arquivos, é respeitar a convenção que os
desenvolvedores JavaScript já utilizam hoje, a utilização dodesenvolvedores JavaScript já utilizam hoje, a utilização do
camelCasecamelCase . Exemplo:. Exemplo: myControl.tsmyControl.ts ..
Com o seu Com o seu arquivo Tarquivo TypeScript criado, atualize-o com o seguinteypeScript criado, atualize-o com o seguinte
trecho de código:trecho de código:
varvar languages : languages : ArrayArray<<stringstring> = [];> = [];
languages.push(languages.push("TypeScript""TypeScript"););
languages.push(3);languages.push(3);
Analisando esse t Analisando esse trecho de código, nrecho de código, nós criamos umós criamos um arrayarray dede stringstring
e estamos adicionando a ele uma string com o valore estamos adicionando a ele uma string com o valor TypeScriptTypeScript ee
um número com o valor 3.um número com o valor 3.
Será que o compilador vaiSerá que o compilador vai transpilar transpilar esse código? Note que nós esse código? Note que nós
temos umtemos um
arrayarray
dede
stringstring
e estamos tentando passar um númeroe estamos tentando passar um númeropara ele.para ele.
Para validar se o compilador vaiPara validar se o compilador vai transpilar transpilar esse código, abra um esse código, abra um
terminal no seu computadorterminal no seu computador, navegue até o , navegue até o diretório em que vocêdiretório em que você
criou o arquivo de configurações e o seu arquivocriou o arquivo de configurações e o seu arquivo .ts.ts e, em seguida,e, em seguida,
execute o comandoexecute o comando tsc tsc nomeDoSeuArqunomeDoSeuArquivo.tsivo.ts , trocando, trocando
"nomeDoSeuArquivo" pelo nome que você deu, no meu caso foi"nomeDoSeuArquivo" pelo nome que você deu, no meu caso foi
"index"."index".
Note que o compiladorNote que o compilador transpiloutranspilou o código e gerou o arquivo o código e gerou o arquivo
index.jsindex.js na mesma pasta dos na mesma pasta dos outros arquivos, mesmo entendendooutros arquivos, mesmo entendendo
que tem um erro no código.que tem um erro no código.
Agora vem aquela Agora vem aquela dúvida: o Tdúvida: o TypeScript não foypeScript não foi desenvolvido pari desenvolvido paraa
prevenir erros como esse prevenir erros como esse em tempo de desenvolvimento?em tempo de desenvolvimento?
A resposta é A resposta é simsim. O TypeScript ajuda a prevenir erros como esse. O TypeScript ajuda a prevenir erros como esse
em tempo de desenvolvimento, mas para que ele possa alertar ouem tempo de desenvolvimento, mas para que ele possa alertar ou
barrar a geração de arquivos com barrar a geração de arquivos com erro, como o do erro, como o do exemplo anteriorexemplo anterior,,
nós devemos informar ao compilador como nós devemos informar ao compilador como nós queremos trabalhar nós queremos trabalhar
com ele através do arquivocom ele através do arquivo tsconfig.jsontsconfig.json . Para isso, abra o seu. Para isso, abra o seu
arquivo e adicione a seguinte configuração nele:arquivo e adicione a seguinte configuração nele:
"compilerOptions""compilerOptions": {: {
/*outras configurações*//*outras configurações*/
"noEmitOnError""noEmitOnError": true,: true,
Essa configuração fará com que o compilador analise o seu códigoEssa configuração fará com que o compilador analise o seu código
e, caso ele encontre algum erro, não crie o arquivoe, caso ele encontre algum erro, não crie o arquivo .js.js ..
Automatizando o compilador Automatizando o compilador
O nosso processo está muiO nosso processo está muito manual. Hto manual. Hoje o nosso exemploje o nosso exemplo temo tem
somente um arquivo, mas quando estivermos somente um arquivo, mas quando estivermos trabalhando em umtrabalhando em um
projeto com mais arquivosprojeto com mais arquivos .ts.ts será necessário executar o comandoserá necessário executar o comando
tsc + nome do arquivotsc + nome do arquivo em cada um deles?em cada um deles?
A resposta é A resposta é nãonão. O . O TTypeScript tem um parâmetro que nósypeScript tem um parâmetro que nóspodemos passar para o compilador ficar podemos passar para o compilador ficar verificando todos osverificando todos os
arquivos do projeto em tempo real, dando feedbacks e, em caso dearquivos do projeto em tempo real, dando feedbacks e, em caso de
sucesso,sucesso, transpilandotranspilando todos de uma vez. todos de uma vez.
Para utilizar esse parâmetro é bem Para utilizar esse parâmetro é bem simples, basta adicionar osimples, basta adicionar o -w-w nana
frente da instruçãofrente da instrução tsctsc ..
VVoltando para o nosso projeto e oltando para o nosso projeto e ainda com o seu ainda com o seu terminal aberto,terminal aberto,
execute o comandoexecute o comando tsc -wtsc -w ..
A seguir você tem uma A seguir você tem uma imagem demonstrandimagem demonstrando o feedback com erro o feedback com erroo
do trecho de código anterior, onde estamos tentando passar umdo trecho de código anterior, onde estamos tentando passar um
número para umnúmero para um arrayarray dede stringstring ::
Figura 1.5: Mensagem de erro no Figura 1.5: Mensagem de erro no compilador.compilador.
Uma outra configuração importante de se destacar é Uma outra configuração importante de se destacar é aa targettarget . Nela,. Nela,
nós passamos qual é a versão do ECMAScript que o nós passamos qual é a versão do ECMAScript que o projeto deveprojeto deve
utilizarutilizar. Como . Como default, ele vem default, ele vem configurado com oconfigurado com o ES5ES5 , mas você, mas você
pode atualizar para outra versão, que vai pode atualizar para outra versão, que vai funcionar normalmente.funcionar normalmente.
Nos próximos capítulos, nós criaremos Nos próximos capítulos, nós criaremos alguns exemplos práticos,alguns exemplos práticos,
em que trabalharemos mais com o arquivoem que trabalharemos mais com o arquivo tsconfig.jsontsconfig.json . Caso você. Caso você
tenha interesse em saber mais tenha interesse em saber mais sobre ele, recomendo a leitura dosobre ele, recomendo a leitura do
seguinte link:seguinte link:
https://wwwhttps://www.typescriptlang.org.typescriptlang.org/docs/handbook/compi/docs/handbook/compiler-options.htmller-options.html..Agora que nós e Agora que nós estamos com o ambiente stamos com o ambiente de desenvolvimentode desenvolvimento
configurado, no próximo capítulo, vamos conhecer os typesconfigurado, no próximo capítulo, vamos conhecer os types
suportados pelo TypeScript.suportados pelo TypeScript.
C 2C 2
Conhecendo os typesConhecendo os types
Conforme vimos na introdução deste livro, o Conforme vimos na introdução deste livro, o TTypeScript é umaypeScript é uma
linguagem tipada, como o C#, Java, entre outras. Neste capítulo,linguagem tipada, como o C#, Java, entre outras. Neste capítulo,
vamos abordar os types que ele suporta através de algunsvamos abordar os types que ele suporta através de alguns
exemplos práticos.exemplos práticos.
2.1 Var, let e const2.1 Var, let e const
Antes de entrarmo Antes de entrarmos nos types, vamos esclarecer us nos types, vamos esclarecer uma das dúvidasma das dúvidas
mais recorrentes dos novos desenvolvedores JavaScript: qual é mais recorrentes dos novos desenvolvedores JavaScript: qual é aa
diferença entrediferença entre varvar ,, letlet ee constconst ??
VVar ar
Até a versão Até a versão ESES55 do JavaScript, a declaração de variáveis do JavaScript, a declaração de variáveis
acontecia por meio da palavra reservadaacontecia por meio da palavra reservada varvar . Essas variáveis eram. Essas variáveis eram
conhecidas como variáveis de escopo.conhecidas como variáveis de escopo.
Um escopo no JavaScript é dado por funções e não por blocos, e Um escopo no JavaScript é dado por funções e não por blocos, e aa
palavra reservadapalavra reservada varvar permite que a variável declarada dentro depermite que a variável declarada dentro de
um escopo seja acessada um escopo seja acessada de qualquer ponto de dentro do código. Ade qualquer ponto de dentro do código. A
seguir você tem um exemplo de declaração de variável utilizando oseguir você tem um exemplo de declaração de variável utilizando o
varvar ::
varvar mensagemForaDoIf = mensagemForaDoIf = 'mensagem fora do if''mensagem fora do if';;
ifif (true) { (true) {
varvar mensagemDentroDoIf = mensagemDentroDoIf = 'mensagem dentro do if''mensagem dentro do if';;
consoleconsole.log(mensagemDentroDoIf);.log(mensagemDentroDoIf);
}}
consoleconsole.log(mensagemForaDoIf);.log(mensagemForaDoIf);
consoleconsole.log(mensagemDentroDoIf);.log(mensagemDentroDoIf);
Executando esse trecho de código, nós Executando esse trecho de código, nós temos o seguinte resultado:temos o seguinte resultado:
Resultado:Resultado:
//mensagem dentro do if//mensagem dentro do if
//mensagem fora do if//mensagem fora do if
//mensagem dentro do if//mensagem dentro do if
Agora vem aquela Agora vem aquela dúvida: se a variádúvida: se a variávelvel mensagemDentroDoIfmensagemDentroDoIf foifoi
declarada dentro dodeclarada dentro do ifif , como nós ainda temos acesso a ela fora , como nós ainda temos acesso a ela fora dodo
bloco de instrução?bloco de instrução?
Para ficar mais claro, vamos a um outro exemplo:Para ficar mais claro, vamos a um outro exemplo:
mensagem =mensagem = 'MSG''MSG';;
consoleconsole.log(mensagem);.log(mensagem);
varvar mensagem; mensagem;
Nesse exemplo, nós estamos Nesse exemplo, nós estamos declarando a variável mensagemdeclarando a variável mensagemdepois de atribuir um valor. Será que ela vai ser depois de atribuir um valor. Será que ela vai ser exibida?exibida?
Resultado:Resultado:
//MSG//MSG
Sim, mas como é possível chamar a Sim, mas como é possível chamar a variável mensagem antesvariável mensagem antes
mesmo de declará-la?mesmo de declará-la?
Isso é possível devido aoIsso é possível devido ao Hoisting Hoisting . Mas o que é. Mas o que é Hoisting Hoisting ? ? NoNo
JavaScript, toda variável é “elevada/içada” (JavaScript, toda variável é “elevada/içada” (hoisting hoisting ) até o topo do) até o topo do
seu contexto de execução. Então esse mecanismo move asseu contexto de execução. Então esse mecanismo move as
variáveis para o topo do seu escopo antes da execução do código.variáveis para o topo do seu escopo antes da execução do código.
No exemplo anterior, a variávelNo exemplo anterior, a variável mensagemDentroDoIfmensagemDentroDoIf está dentro deestá dentro de
umauma functionfunction , então sua declaração é elevada para o topo do seu, então sua declaração é elevada para o topo do seu
contexto, ou seja, para o topo dacontexto, ou seja, para o topo da functionfunction ..
É por esse motivo que é possível acessá-la antes de ela É por esse motivo que é possível acessá-la antes de ela ter sidoter sido
declarada.declarada.
LetLet
A palavra reservada A palavra reservada letlet é usada para declarar variáveis comé usada para declarar variáveis com
escopo de bloco. Seu comportamento é escopo de bloco. Seu comportamento é idêntico aoidêntico ao varvar quandoquando
declarada fora de umadeclarada fora de uma functionfunction , isto é, ela fica acessível no escopo, isto é, ela fica acessível no escopo
global.global.
Mas, quando declarada dentro de qualquer bloco, seja ele umaMas, quando declarada dentro de qualquer bloco, seja ele umafunctionfunction , um, um ifif ou umou um looploop , ela fica acessível apenas dentro do, ela fica acessível apenas dentro do
bloco (e sub-blocos) em que bloco (e sub-blocos) em que foi declarada.foi declarada.
Para ficar mais claro, vamos atualizar o exemplo anterior comPara ficar mais claro, vamos atualizar o exemplo anterior com letlet ..
letlet mensagemForaDoIf = mensagemForaDoIf = 'mensagem fora do if''mensagem fora do if';;
ifif (true) { (true) {
letlet mensagemDentroDoIf = mensagemDentroDoIf = 'mensagem dentro do if''mensagem dentro do if';;
consoleconsole.log(mensagemDentroDoIf);.log(mensagemDentroDoIf);
}}
consoleconsole.log(mensagemForaDoIf);.log(mensagemForaDoIf);
consoleconsole.log(mensagemDentroDoIf);.log(mensagemDentroDoIf);
Resultado:Resultado:
//mensagem dentro do if//mensagem dentro do if
//mensagem fora do if//mensagem fora do if
//ReferenceEr//ReferenceError: mensagemDentroDoIf is ror: mensagemDentroDoIf is not definednot defined
Note que retornaram as duas mensagens de dentro e fora doNote que retornaram as duas mensagens de dentro e fora do ifif ee
uma mensagem de erro na última linha. Isso ocorreu porque auma mensagem de erro na última linha. Isso ocorreu porque a
variável ficou limitada ao escopo dovariável ficou limitada ao escopo do ifif ..
ConstConst
A palavra reservada A palavra reservada constconst é usada para declarar variáveisé usada para declarar variáveis read-read-
onlyonly, isto é, a variável não pode ter o seu , isto é, a variável não pode ter o seu valor alterado, seu estadovalor alterado, seu estado
é imutável. Assim como as variáveis declaradas comoé imutável. Assim como as variáveis declaradas como letlet ee constconst
também ficam limitadas a um escopo.também ficam limitadas a um escopo.
constconst mensagem = mensagem = 'MSG 1''MSG 1';;
consoleconsole.log(mensagem);.log(mensagem); // MSG 1// MSG 1
mensagem =mensagem = 'MSG 2''MSG 2';; // TypeError: Assignment to constant variable.// TypeError: Assignment to constant variable.
Resultado:Resultado:
// MSG 1// MSG 1
// TypeError: Assignment to constant variable.// TypeError: Assignment to constant variable.
2.2 Boolean2.2 Boolean
Agora entrand Agora entrando nos types, iniciareo nos types, iniciaremos com os tipos booleamos com os tipos booleanos.nos.
Como em outras linguagens tipadas, o Boolean suporta dois Como em outras linguagens tipadas, o Boolean suporta dois tipostipos
de valores:de valores: truetrue ouou falsefalse ..
letlet ativo : ativo : booleanboolean;;
ativo = false;ativo = false;
ativo = true;ativo = true;
Nós também podemos declarar uma variável sem o seu type, bastaNós também podemos declarar uma variável sem o seu type, bastapassar o seu valor inicial:passar o seu valor inicial:
letlet ativo : ativo : booleanboolean = true; = true;
2.3 Number 2.3 Number
No TypeSNo TypeScript, todos os valores cript, todos os valoresnuméricos, comonuméricos, como floatingfloating ,, decimaldecimal ,,
hexhex ,, octaloctal devem ser tipados comodevem ser tipados como numbernumber ..
A seguir você tem um e A seguir você tem um exemplo com os tipos nuxemplo com os tipos numéricos suportados:méricos suportados:
letlet octal: octal: numbernumber = 0o745; = 0o745;
letlet binary: binary: numbernumber = 0b1111; = 0b1111;
letlet decimal: decimal: numbernumber = 34; = 34;
letlet hex: hex: numbernumber = 0xf34d; = 0xf34d;
Ou como vimos acima no exemplo com Ou como vimos acima no exemplo com os Booleans:os Booleans:
letlet octal = 0o745; octal = 0o745;
letlet binary = 0b1111; binary = 0b1111;
letlet decimal = 34; decimal = 34;
letlet hex = 0xf34d; hex = 0xf34d;
2.4 String2.4 String
As strings armazenam valo As strings armazenam valores do tipo textores do tipo texto. Diferente de o. Diferente de outrasutras
linguagens de programação, no linguagens de programação, no JavaScript/TJavaScript/TypeScript nós ypeScript nós podemospodemos
declarar uma string em aspas simples e aspas duplas.declarar uma string em aspas simples e aspas duplas.
letlet cor: cor: stringstring = = "verde""verde";;
cor =cor = 'azul''azul';;
Nós também podemos declarar uma vNós também podemos declarar uma variável do tipo string utilizandoariável do tipo string utilizando
template stringstemplate strings, dessa forma nós podemos concatenar valores:, dessa forma nós podemos concatenar valores:
letlet nome: nome: stringstring = = 'Anders Hejlsberg''Anders Hejlsberg';;
letlet idade: idade: numbernumber = 58; = 58;
letlet sentence: sentence: stringstring = `Olá, meu nome é ${ nome }, eu = `Olá, meu nome é ${ nome }, eu tenho ${idade} anos.tenho ${idade} anos.
2.5 Trabalhando com Strings2.5 Trabalhando com Strings
É muito comum a manipulação de uma string no nosso dia a dia É muito comum a manipulação de uma string no nosso dia a dia dede
desenvolvimento. Em alguns cenários, nós precisaremos criar desenvolvimento. Em alguns cenários, nós precisaremos criar
alguns métodos para resolver uma determinada demanda, mas emalguns métodos para resolver uma determinada demanda, mas em
outros cenários nós podemos utilizar alguns já outros cenários nós podemos utilizar alguns já disponíveis nadisponíveis na
linguagem. A seguir destacarei alguns dos métodos e linguagem. A seguir destacarei alguns dos métodos e propriedadespropriedades
que eu acredito serem os mais utilizados.que eu acredito serem os mais utilizados.
LengthLength
Adicionando a Adicionando a palavra reservadapalavra reservada lengthlength no final de uma string, nósno final de uma string, nós
temos o tamanho dela.temos o tamanho dela.
Para ficar mais claro, vamos verificar o tamanho da nossa string,Para ficar mais claro, vamos verificar o tamanho da nossa string,
que foi concatenada no exemplo anterior com a que foi concatenada no exemplo anterior com a variávelvariável sentencesentence ..
//outras variáveis//outras variáveis
letlet sentence: sentence: stringstring = `Olá, meu nome é ${ nome }, eu tenho ${idade} = `Olá, meu nome é ${ nome }, eu tenho ${idade}
anos.`;anos.`;
consoleconsole.log(sentence.length);.log(sentence.length); //Resultado 51//Resultado 51
IndexOf IndexOf
O métodoO método IndexOfIndexOf nos permite encontrar a posição de um caracterenos permite encontrar a posição de um caractere
ou string. Ele pode ser ou string. Ele pode ser utilizado para recuperar a posição inicial deutilizado para recuperar a posição inicial de
um elemento, dentro de uma sequência de um elemento, dentro de uma sequência de caracteres. Caso essecaracteres. Caso esse
elemento não exista, é retornado o valor elemento não exista, é retornado o valor -1, caso ele exista, -1, caso ele exista, retornaretornaa sua posição.a sua posição.
Para ficar mais claro, vamos utilizar esse método no nosso exemploPara ficar mais claro, vamos utilizar esse método no nosso exemplo
anterior, para que ele retorne a posição da palavraanterior, para que ele retorne a posição da palavra nomenome dentro dadentro da
variávelvariável sentencesentence ..
//outras variáveis//outras variáveis
letlet sentence: sentence: stringstring = `Olá, meu nome é ${ nome }, eu tenho ${idade} = `Olá, meu nome é ${ nome }, eu tenho ${idade}
anos.`;anos.`;
consoleconsole.log(sentence.indexOf(.log(sentence.indexOf('nome''nome'));)); //posição 9//posição 9
Caso a gente procure por uma palavra ou caractere que não existe,Caso a gente procure por uma palavra ou caractere que não existe,
ele deve retornarele deve retornar -1-1 conforme mencionado acima:conforme mencionado acima:
//outras variáveis//outras variáveis
letlet sentence: sentence: stringstring = `Olá, meu nome é ${ nome }, eu tenho ${idade} = `Olá, meu nome é ${ nome }, eu tenho ${idade}
anos.`;anos.`;
consoleconsole.log(sentence.indexOf(.log(sentence.indexOf('idade''idade'));)); //retorno -1//retorno -1
consoleconsole.log(sentence.indexOf(.log(sentence.indexOf('!''!'));)); //retorno -1//retorno -1
Note que ele retornouNote que ele retornou -1-1 parapara idadeidade . Isso aconteceu porque essa. Isso aconteceu porque essa
palavra é uma variável e não um valor dentro da string.palavra é uma variável e não um valor dentro da string.
2.6 Array2.6 Array
Como em outras linguagens de programação, nós declaramos umComo em outras linguagens de programação, nós declaramos um
array no TypeScript utilizando as chavesarray no TypeScript utilizando as chaves [][] ..
letlet numeros: numeros: numbernumber[] = [1, 2, 3];[] = [1, 2, 3];
letlet textos : textos : stringstring[] = [[] = ["exemplo 1""exemplo 1",, "exemplo 2""exemplo 2",, "exemplo 3""exemplo 3"];];
Ou nós podemos utilizar a Ou nós podemos utilizar a palavra reservadapalavra reservada Array<>Array<> , como no, como no
exemplo a seguir:exemplo a seguir:
letlet numeros: numeros: ArrayArray<<numbernumber> = [1, 2, 3];> = [1, 2, 3];
letlet textos: textos: ArrayArray<<stringstring> = [> = ["exemplo 1""exemplo 1",, "exemplo 2""exemplo 2",, "exemplo 3""exemplo 3"];];
Para adicionar um novo item Para adicionar um novo item ao arrayao array, nós podemos utilizar a, nós podemos utilizar a
palavra reservadapalavra reservada pushpush ..
letlet numeros: numeros: ArrayArray<<numbernumber> = [1, 2, 3];> = [1, 2, 3];
letlet textos: textos: ArrayArray<<stringstring> = [> = ["exemplo 1""exemplo 1",, "exemplo 2""exemplo 2",, "exemplo 3""exemplo 3"];];
numeros.push(4);numeros.push(4);
textos.push(textos.push("exemplo 3""exemplo 3"););
consoleconsole.log(numeros);.log(numeros);
consoleconsole.log(textos);.log(textos);
//Resultado//Resultado
[ 1, 2, 3, 4 ][ 1, 2, 3, 4 ]
[[ 'exemplo 1''exemplo 1',, 'exemplo 2''exemplo 2',, 'exemplo 3''exemplo 3',, 'exemplo 3''exemplo 3' ] ]
2.7 ReadonlyArray2.7 ReadonlyArray
OO ReadonlyArray<T>ReadonlyArray<T> é um array que nos permite somente leitura. Eleé um array que nos permite somente leitura. Ele
remove todos os métodos de remove todos os métodos de alteração de um arrayalteração de um array, como, como pushpush ,,
poppop etc.etc.
letlet numerosDaMega: ReadonlyArray<numerosDaMega: ReadonlyArray<numbernumber> = [8, 5, 5, 11, 4, 28];> = [8, 5, 5, 11, 4, 28];numerosDaMeganumerosDaMega[0] = [0] = 12;12; // error!// error!
numerosDaMega.push(23);numerosDaMega.push(23); // error!// error!
numerosDaMega.pop();numerosDaMega.pop(); // error!// error!
numerosDaMeganumerosDaMega.length = .length = 100;100; // error!// error!
Caso você tente transpilar esse Caso você tente transpilar esse código, ele vai gerar os código, ele vai gerar os seguintesseguintes
erros no seu console:erros no seu console:
//Resultado://Resultado:
index.ts:2:1 - error TS2542: Index signatureindex.ts:2:1 - error TS2542: Index signature inin type type 'readonly number[]''readonly number[]'
only permits reading.only permits reading.
2 numerosDaMega[0] = 12;2 numerosDaMega[0] = 12;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index.ts:3:15 - error TS2339: Propertyindex.ts:3:15 - error TS2339:Property 'push''push' does not exist on type does not exist on type
'readonly number[]''readonly number[]'..
3 3 numerosDaMega.numerosDaMega.push(23);push(23);
~~~~~~~~
index.ts:4:15 - error TS2339: Propertyindex.ts:4:15 - error TS2339: Property 'pop''pop' does not exist on type does not exist on type
'readonly number[]''readonly number[]'..
4 4 numerosDaMega.numerosDaMega.pop();pop();
~~~~~~
index.ts:5:15 - error TS2540: Cannot assign toindex.ts:5:15 - error TS2540: Cannot assign to 'length''length' because it is a because it is a
read-only property.read-only property.
5 numerosDaMega.length = 100;5 numerosDaMega.length = 100;~~~~~~~~~~~~
Found 4 errors.Found 4 errors.
À versão 3.4 do T À versão 3.4 do TypeScript, foi adiypeScript, foi adicionada uma nova sicionada uma nova sintaxe para ontaxe para o
ReadonlyArray<T>ReadonlyArray<T> . Nessa versão, . Nessa versão, o time que cuida o time que cuida do Typdo TypeScripteScript
simplificou a declaração dossimplificou a declaração dos ReadonlyArray<T>ReadonlyArray<T> parapara Readonly<T>Readonly<T> ..
A seguir você tem o A seguir você tem o exemplo com o arrayexemplo com o array numerosDaMeganumerosDaMega atualizadoatualizado
com essa nova sintaxecom essa nova sintaxe Readonly<T>Readonly<T> ::
letlet numerosDaMega: Readonly< numerosDaMega: Readonly<numbernumber[]> = [8, 5, 5, 11, 4, 28];[]> = [8, 5, 5, 11, 4, 28];
Acredito que ne Acredito que neste momento você deva ste momento você deva estar se perguntanestar se perguntando: Ondedo: Onde
eu poderia utilizar essa eu poderia utilizar essa funcionalidadefuncionalidade??
Pensando nisso, eu pesquisei algumas bibliotecas no portalPensando nisso, eu pesquisei algumas bibliotecas no portal NPMNPM ee
encontrei uma que exporta dadosencontrei uma que exporta dados jsonjson para um arquivopara um arquivo .csv.csv
chamadachamada json2csvjson2csv . Link da biblioteca:. Link da biblioteca: json2csv json2csv((https://www.npmjs.com/package/json2csvhttps://www.npmjs.com/package/json2csv).).
Analisando seus mé Analisando seus métodos, encontrei otodos, encontrei o parseAsyncparseAsync , que implementa, que implementa
Readonly<T>Readonly<T> ::
exportexport function parseAsync<T>function parseAsync<T>((
data: Readonly<T> | ReadonlyArray<T> | Readable,data: Readonly<T> | ReadonlyArray<T> | Readable,
opts?: opts?: json2csv.Optiojson2csv.Options<T>,ns<T>,
transformOpts?transformOpts?: : TransformOptTransformOptionsions
)): : Promise<stringPromise<string>;>;
2.8 Tuple2.8 Tuple
As tuplas, ou As tuplas, ou tupletuple no inglês, representam uma no inglês, representam uma estrutura de dadosestrutura de dados
simples semelhante a um array. A grande diferença entre eles é quesimples semelhante a um array. A grande diferença entre eles é que
nos arrays nós trabalhamos somente com um tipo de dado,nos arrays nós trabalhamos somente com um tipo de dado,
enquanto com as tuplas temos enquanto com as tuplas temos diferentes tipos.diferentes tipos.
letlet list: [ list: [stringstring,, numbernumber,, stringstring] = ] = [['string''string', 1,, 1, 'string 2''string 2'];];
Uma das novidades da vUma das novidades da versão 4.0 do TypeScript é a possibilidadeersão 4.0 do TypeScript é a possibilidade
de incluir nomes nos parâmetros das nossas tuplas. Essa é uma dasde incluir nomes nos parâmetros das nossas tuplas. Essa é uma das
novidades que, na minha opinião, pode ajudar a deixar o novidades que, na minha opinião, pode ajudar a deixar o nossonosso
código mais legível.código mais legível.
letlet list: [nome: list: [nome: stringstring, idade:, idade: numbernumber, email:, email: stringstring] = [] = ['Bill Gates''Bill Gates',,
65,65, 'bill@teste.com''bill@teste.com'];];
Da mesma forma que nos arrays, para adicionar um novo registroDa mesma forma que nos arrays, para adicionar um novo registro
em uma tupla, nós utilizamos oem uma tupla, nós utilizamos o .push().push() ..
letlet list: [ list: [stringstring,, numbernumber] = [] = ['Bill Gates''Bill Gates', 1];, 1];
list.push(list.push('Steve''Steve', 2);, 2);
Agora para testa Agora para testar um desses valores, podr um desses valores, podemos utilizar os seusemos utilizar os seus
índices.índices.
letlet list: [ list: [stringstring,, numbernumber] = [] = ['Bill Gates''Bill Gates', 1];, 1];
consoleconsole.log(list[0]);.log(list[0]); //Bill//Bill
consoleconsole.log(list[1]);.log(list[1]); //1//1
//Resultado://Resultado:
//Bill//Bill
//1//1
Assim como nos arrays, nós ta Assim como nos arrays, nós também podemos trabambém podemos trabalhar com alhar com a
palavra reservadapalavra reservada readonlyreadonly com ascom as tuplastuplas ..
letlet list: readonly [ list: readonly [stringstring,, numbernumber] = [] = ['Bill Gates''Bill Gates', 1];, 1];
list.push(list.push('Steve''Steve', 2);, 2);
//Resultado://Resultado:
index.ts:2:6 - error TS2339: Propertyindex.ts:2:6 - error TS2339: Property 'push''push' does not exist on type does not exist on type
'readonly [string, number]''readonly [string, number]'..
2 list.push(2 list.push('Steve''Steve', 2);, 2);
2.9 Enum2.9 Enum
OO enumenum nos permite declarar um conjunto de nos permite declarar um conjunto de valores/constantesvalores/constantes
predefinidos. Existem três formas de se trabalhar com ele nopredefinidos. Existem três formas de se trabalhar com ele no
TypeScript:TypeScript:
Number.Number.
String.String.
HH etereterogenogeneous.eous.
NuméricoNumérico
Os enums numéricos armazenam strings com valores numéricos.Os enums numéricos armazenam strings com valores numéricos.
Nós podemos declará-los com um valor inicial.Nós podemos declará-los com um valor inicial.
exportexport enumenum DiaDaSemana { DiaDaSemana {
Segunda = 1,Segunda = 1,
Terca = 2,Terca = 2,
Quarta = 3,Quarta = 3,
Quinta = 4,Quinta = 4,
Sexta = 5,Sexta = 5,
Sabado = 6,Sabado = 6,
Domingo = 7Domingo = 7
}}
Caso você não passe um valor inicial na declaração do seuCaso você não passe um valor inicial na declaração do seu enumenum , o, o
compilador do Tcompilador do TypeScript fará um autoincremento de +ypeScript fará um autoincremento de +1 iniciando1 iniciando
em 0 até o último elemento doem 0 até o último elemento do enumenum ..
exportexport enumenum DiaDaSemana { DiaDaSemana {
Segunda,Segunda,
Terca,Terca,
Quarta,Quarta,
Quinta,Quinta,
Sexta,Sexta,
Sabado,Sabado,
DomingoDomingo
}}
Ou, no caso de passarmos um valor numérico inicial, ele fará comoOu, no caso de passarmos um valor numérico inicial, ele fará como
no passo anteriorno passo anterior, incrementando +, incrementando +1 até o último elemento:1 até o último elemento:
exportexport enumenum DiaDaSemana { DiaDaSemana {
Segunda = 18,Segunda = 18,
Terca,Terca,
Quarta,Quarta,
Quinta,Quinta,
Sexta,Sexta,
Sabado,Sabado,
DomingoDomingo
}}
Para acessar um valor dentro de umPara acessar um valor dentro de um enumenum , nós podemos utilizar , nós podemos utilizar
uma das formas a seguir:uma das formas a seguir:
letlet dia = dia = DiaDaSemana[1DiaDaSemana[19];9]; // Terca// Terca
letlet diaNumero = diaNumero = DiaDaSemana[diDiaDaSemana[dia];a]; // 19// 19
letlet diaString= DiaDaSemana[ diaString= DiaDaSemana["Segunda""Segunda"];]; // 18// 18
Resultado:Resultado:
// Terca// Terca
// 19// 19
// 18// 18
StringsStrings
Diferente dos enums numéricos, os enums do tipo string precisamDiferente dos enums numéricos, os enums do tipo string precisam
iniciar com um valor.iniciar com um valor.
exportexport enumenum DiaDaSemana { DiaDaSemana {
Segunda =Segunda = "Segunda-feira""Segunda-feira",,
Terca =Terca = "Terça-feira""Terça-feira",,
Quarta =Quarta = "Quarta-feira""Quarta-feira",,
Quinta =Quinta = "Quinta-feira""Quinta-feira",,
Sexta =Sexta = "Sexta-feira""Sexta-feira",,
Sabado =Sabado = "Sábado""Sábado",,
Domingo =Domingo = "Domingo""Domingo",,
}}
Para acessar os valores de umPara acessar os valores de um enumenum do tipo string, basta utilizar do tipo string, basta utilizar
uma das formas a seguir:umadas formas a seguir:
consoleconsole.log(DiaDaSemana.Sexta);.log(DiaDaSemana.Sexta); //Sexta-feira//Sexta-feira
consoleconsole.log(DiaDaSemana[.log(DiaDaSemana['Sabado''Sabado']);]); //Sábado//Sábado
Resultado:Resultado:
//Sexta-feira//Sexta-feira
//Sábado//Sábado
HeterogeneousHeterogeneous
Os enums HOs enums H eterogeneous são eterogeneous são pouco conhecidos. pouco conhecidos. Eles aceitam osEles aceitam os
dois tipos de valores: strings e números.dois tipos de valores: strings e números.
exportexport enumenum Heterogeneous { Heterogeneous {
Segunda =Segunda = 'Segunda-feira''Segunda-feira',,
Terca = 1,Terca = 1,
Quarta,Quarta,
Quinta,Quinta,
Sexta,Sexta,
Sabado,Sabado,
Domingo = 18,Domingo = 18,
}}
E como acessar esses valores? A seguir você tem um exemploE como acessar esses valores? A seguir você tem um exemplo
demonstrando esse passo:demonstrando esse passo:
consoleconsole.log(Heterogeneous.Segunda);.log(Heterogeneous.Segunda);
consoleconsole.log(Heterogeneous[.log(Heterogeneous['Segunda''Segunda']);]);
consoleconsole.log(Heterogeneous[.log(Heterogeneous['Terca''Terca']);]);
consoleconsole.log(Heterogeneous[1]);.log(Heterogeneous[1]);
consoleconsole.log(Heterogeneous[.log(Heterogeneous['Quarta''Quarta']);]);
consoleconsole.log(Heterogeneous[.log(Heterogeneous['Quinta''Quinta']);]);
consoleconsole.log(Heterogeneous[.log(Heterogeneous['Sexta''Sexta']);]);
consoleconsole.log(Heterogeneous[.log(Heterogeneous['Sabado''Sabado']);]);
consoleconsole.log(Heterogeneous[.log(Heterogeneous['Domingo''Domingo']);]);
Resultado:Resultado:
//Segunda-feira//Segunda-feira
//Segunda-feira//Segunda-feira
//1//1
//Terca//Terca
//2//2
//3//3
//4//4
//5//5
//18//18
2.10 Union2.10 Union
OO unionunion nos permite combinar um ou mais tipos. Sua sintaxe é umnos permite combinar um ou mais tipos. Sua sintaxe é um
pouco diferente dos outros types, ele utiliza pouco diferente dos outros types, ele utiliza uma barra vertical parauma barra vertical para
passar os tipos que ele deve aceitar.passar os tipos que ele deve aceitar.
Sintaxe:Sintaxe:
(tipo1| tipo2 ...)(tipo1| tipo2 ...)
Para ficar mais claro, vamos a alguns exemplos:Para ficar mais claro, vamos a alguns exemplos:
letlet exemploVariavel: ( exemploVariavel: (stringstring | | numbernumber););
exemploVariavexemploVariavel = el = 123;123;
consoleconsole.log(exemploVariavel);.log(exemploVariavel);
exemploVariavexemploVariavel el == "ABC""ABC";;
consoleconsole.log(exemploVariavel);.log(exemploVariavel);
Resultado:Resultado:
//123//123
//ABC//ABC
Note que a variávelNote que a variável exemploVariavelexemploVariavel aceitou os dois valores:aceitou os dois valores: 123123
(número) e(número) e "ABC""ABC" (string).(string).
Agora vem aquela Agora vem aquela dúvida: nós não dúvida: nós não podemos passar mais dpodemos passar mais de doise dois
tipos para otipos para o unionunion ? Caso você também tenha pensado nesse? Caso você também tenha pensado nesse
cenário, a resposta é "sim". A seguir você tem um exemplocenário, a resposta é "sim". A seguir você tem um exemplo
demonstrando como passar três tipos para o typedemonstrando como passar três tipos para o type unionunion ::
letlet exemploVariavel: ( exemploVariavel: (stringstring | | numbernumber | | booleanboolean););
exemploVariavexemploVariavel = el = 123;123;
consoleconsole.log(exemploVariavel);.log(exemploVariavel);
exemploVariavexemploVariavel el == "ABC""ABC";;
consoleconsole.log(exemploVariavel);.log(exemploVariavel);
exemploVariavexemploVariavel = el = true;true;
consoleconsole.log(exemploVariavel);.log(exemploVariavel);
Resultado:Resultado:
//123//123
//ABC//ABC
//true//true
Mas oMas o unionunion não fica limitado à utilização de strings, números enão fica limitado à utilização de strings, números e
booleans. Nós também podemos passar umbooleans. Nós também podemos passar um arrayarray para ele:para ele:
varvar arr: ( arr: (numbernumber[] |[] | stringstring[]);[]);
varvar i: i: numbernumber;;
arr = [1, 2, 4]arr = [1, 2, 4]
forfor (i = 0; i < arr.length; i++) { (i = 0; i < arr.length; i++) {
consoleconsole.log(arr[i]).log(arr[i])
}}
arr = [arr = ["A""A",, "B""B",, "C""C",, "D""D"]]
forfor (i = 0; i < arr.length; i++) { (i = 0; i < arr.length; i++) {
consoleconsole.log(arr[i]).log(arr[i])
}}
Como parâmetro de funções:Como parâmetro de funções:
function deleteTestefunction deleteTeste(usuario: string | string[])(usuario: string | string[]) {{
ifif ( (typeoftypeof usuario == usuario == "string""string") {) {
consoleconsole.log(usuario,.log(usuario, "deletado""deletado"););
}} elseelse { {
varvar i; i;
forfor (i = 0; i < usuario.length; i++) { (i = 0; i < usuario.length; i++) {
consoleconsole.log(usuario[i],.log(usuario[i], "deletado""deletado"););
}}
}}
Resultado:Resultado:
//função para deletar um registro//função para deletar um registro
//função para deletar mais de um registro//função para deletar mais de um registro
Nesse exemplo, nós podemos passar um registro para o nossoNesse exemplo, nós podemos passar um registro para o nosso
métodométodo deleteTestedeleteTeste ou passar um array de registros para seremou passar um array de registros para serem
deletados.deletados.
Note que nós temos Note que nós temos uma nova palavra reservada chamadauma nova palavra reservada chamada typeoftypeof
no exemplo anteriorno exemplo anterior, onde criamos uma , onde criamos uma função que recebe um tipofunção que recebe um tipo
unionunion
como parâmetro. Ocomo parâmetro. O
typeoftypeof
é um tipo de guarda, ou, no inglês,é um tipo de guarda, ou, no inglês,
Type Guard Type Guard , do JavaScript, que nós podemos utilizar desde a, do JavaScript, que nós podemos utilizar desde a
versão 1.4 do TypeScript.versão 1.4 do TypeScript.
Ele é utilizado em cenários onde precisamos verificar o tipo de umEle é utilizado em cenários onde precisamos verificar o tipo de um
objeto dentro de um bloco condicional, comoobjeto dentro de um bloco condicional, como ifif ou oou o switch/caseswitch/case ..
Para ficar mais claro, vamos criar um exemplo.Para ficar mais claro, vamos criar um exemplo.
letlet x: x: stringstring | | numbernumber | | booleanboolean = 13; = 13;
consoleconsole.log(.log(typeoftypeof(x))(x))
//Resultado//Resultado
numbernumber
Além do Além do typeoftypeof , temos outro tipo , temos outro tipo de guarda chamadode guarda chamado instanceofinstanceof ..
Ele tem a Ele tem a mesma funcionalidade domesma funcionalidade do typeoftypeof , com a diferença de que, com a diferença de que
oo typeoftypeof retorna o tipo do objeto e oretorna o tipo do objeto e o instanceofinstanceof retorna umretorna um booleanboolean ..
interfaceinterface Z { x(): Z { x(): stringstring } }
classclass A A implementsimplements Z { Z {
x():x(): stringstring { {
throwthrow newnew ErrorError(("Method not "Method not implemented."implemented."););
}}
}}
classclass B B implementsimplements Z { Z {
x():x(): stringstring { {
throwthrow newnew ErrorError(("Method not "Method not implemented."implemented."););
}}
}}
function function exemploComInstexemploComInstanceofanceof(paramentro: Z)(paramentro: Z) {{
ifif (paramentro (paramentro instanceofinstanceof A) { A) {
consoleconsole.log(.log("Sou a classe A""Sou a classe A"););
}}
elseelse ifif (paramentro (paramentro instanceofinstanceof B) { B) {
consoleconsole.log(.log("Sou a classe B""Sou a classe B"););
}}
}}
exemploComInstanceof(exemploComInstanceof(newnew A()); A());
Bem simples, não é? Nesse exemplo, nós criamos uma funçãoBem simples, não é? Nesse exemplo, nós criamos uma função
chamadachamada exemploComInstanceofexemploComInstanceof , que recebe uma , que recebe uma interface chamadainterface chamada
dede ZZ , e validamos se a classe , e validamos se a classe que está sendo passada noque está sendo passada no
parâmetro é a classeparâmetro é a classe AA ou aou a BB ..
Para quem está tendo o seu primeiro contato com as palavrasPara quem está tendo o seu primeirocontato com as palavras
reservadas, interfaces e classes neste reservadas, interfaces e classes neste capítulo, não se preocupe emcapítulo, não se preocupe em
entender o que elas são agora, nós abordaremos esses temas noentender o que elas são agora, nós abordaremos esses temas no
decorrer deste livro.decorrer deste livro.
2.11 Any2.11 Any
OO anyany é um dos types que mais cé um dos types que mais causa confusão quando nósausa confusão quando nós
estamos iniciando com o TypeScript. Como o TS é estamos iniciando com o TypeScript. Como o TS é tipado e nóstipado e nós
temos otemos o UnionUnion em casos de mais de um em casos de mais de um valorvalor, a maioria dos, a maioria dos
desenvolvedores iniciantes em TypeScript se pergunta: Por quedesenvolvedores iniciantes em TypeScript se pergunta: Por que
utilizar outilizar o anyany ??
Imagine o seguinte cenário: você está fazendo Imagine o seguinte cenário: você está fazendo integração a umaintegração a uma
API de terceiros e API de terceiros e, mesmo que você tenh, mesmo que você tenha uma documentaçãoa uma documentação
dessa API, você não conhece 100% a estrutura do outro projeto.dessa API, você não conhece 100% a estrutura do outro projeto.
Esse é um dos cenários em Esse é um dos cenários em que oque o anyany é mais utilizado.é mais utilizado.
Para ficar mais claro como nós podemos utilizar o typePara ficar mais claro como nós podemos utilizar o type anyany , vamos, vamos
a um exemplo prático.a um exemplo prático.
letlet variavelAny: variavelAny: anyany = = "Variável""Variável";;
variavelAny = 34;variavelAny = 34;
variavelAny = true;variavelAny = true;
Note que no exemplo anterior a Note que no exemplo anterior a variávelvariável variavelAnyvariavelAny recebeu trêsrecebeu três
valores diferentes: uma string, um número e valores diferentes: uma string, um número e um bollean. Emum bollean. Em
cenários como esse é que podemos utilizar o typecenários como esse é que podemos utilizar o type anyany ..
2.12 Tipando funções2.12 Tipando funções
O TypeScripO TypeScript também nos permite t também nos permite tipar as nossas funçõestipar as nossas funções
informando para o compilador qual é o tipo que elas devem retornar.informando para o compilador qual é o tipo que elas devem retornar.
A seguir você tem um t A seguir você tem um trecho de código corecho de código com uma função que rm uma função que recebeecebe
dois parâmetrosdois parâmetros numbernumber e retorna uma string:e retorna uma string:
function calcfunction calc(x: number, y: number)(x: number, y: number): string: string {{
returnreturn `resultado: ${x + y}`; `resultado: ${x + y}`;
}}
Funções como tiposFunções como tipos
No TypeSNo TypeScript, podemos tipar uma variável com cript, podemos tipar uma variável com o valor de umao valor de uma
função. Pegando o nosso exemplo anterior, vamos atribuir o valor dafunção. Pegando o nosso exemplo anterior, vamos atribuir o valor da
function calcfunction calc a uma variável.a uma variável.
function calcfunction calc(x: number, y: number)(x: number, y: number): string: string {{
returnreturn `resultado: ${x + y}`; `resultado: ${x + y}`;
}}
letlet resultado : resultado : stringstring;;
resultado = calc(10,15);resultado = calc(10,15);
consoleconsole.log(resultado);.log(resultado);
Resultado:Resultado:
//resultado: 25//resultado: 25
Caso você tente atribuir esse valor a uma variável de um tipoCaso você tente atribuir esse valor a uma variável de um tipo
diferente de string, o compilador retornará a mensagem dediferente de string, o compilador retornará a mensagem de erroerro::
function calcfunction calc(x: number, y: number)(x: number, y: number): string: string {{
returnreturn `resultado: ${x + y}`; `resultado: ${x + y}`;
}}
letlet resultado : resultado : numbernumber;;
resultado = calc(10,15);resultado = calc(10,15);
consoleconsole.log(resultado);.log(resultado);
Resultado:Resultado:
//Type 'string' is not //Type 'string' is not assignable to type 'number'.ts(2322)assignable to type 'number'.ts(2322)
2.13 Void2.13 Void
O typeO type voidvoid é bastante utilizado em funções. Diferente do typeé bastante utilizado em funções. Diferente do type anyany ,,
que espera o retorno de qualquer valor, oque espera o retorno de qualquer valor, o voidvoid passa para opassa para o
compilador que aquela função não terá nenhum retorno.compilador que aquela função não terá nenhum retorno.
function logfunction log()(): void: void {{
consoleconsole.log(.log('Sem retorno''Sem retorno'););
}}
2.14 Never 2.14 Never
Embora o typeEmbora o type nevernever tenha sido adicionado à versão 2.0 dotenha sido adicionado à versão 2.0 do
TTypeScript, ele é ypeScript, ele é pouco conhecido pelos desenvolvedores. Essepouco conhecido pelos desenvolvedores. Esse
type indica que algo nunca deve ocorrer. Para que você possa ter type indica que algo nunca deve ocorrer. Para que você possa ter
uma ideia de como utilizá-lo no seu dia a dia, vamos a algunsuma ideia de como utilizá-lo no seu dia a dia, vamos a alguns
exemplos práticos.exemplos práticos.
Nós podemos utilizá-lo em funções comNós podemos utilizá-lo em funções com exceptionexception::
function function verificandoTipverificandoTipoo(x: string | number)(x: string | number): boolean: boolean {{
ifif ( (typeoftypeof x === x === "string""string") {) {
returnreturn true; true;
}} elseelse ifif ( (typeoftypeof x === x === "number""number") {) {
returnreturn false; false;
}}
returnreturn fail( fail("Esse método não aceita esse tipo de "Esse método não aceita esse tipo de type!"type!"););
}}
function failfunction fail(message: string)(message: string): never: never {{ throwthrow newnew ErrorError(message); }(message); }
Nesse exemplo, nós estamos recebendo via parâmetro um typeNesse exemplo, nós estamos recebendo via parâmetro um type
unionunion (string e número), que deve (string e número), que deve retornar um boolean. Caso sejaretornar um boolean. Caso seja
passado um valor que não seja uma string ou um número, apassado um valor que não seja uma string ou um número, a
chamada entra em uma outra chamada entra em uma outra função chamadafunção chamada failfail , que retornará, que retornará
uma exceção.uma exceção.
verificandoTipo(verificandoTipo("teste String""teste String"););
verificandoTipo(10);verificandoTipo(10);
letlet ativo = true; ativo = true;
verificandoTipo(ativo);verificandoTipo(ativo);
Resultado:Resultado:
//Ok//Ok
//Ok//Ok
// retorna uma exception com a mensagem: Esse método não // retorna uma exception com a mensagem: Esse método não aceita esse tipoaceita esse tipo
de type!de type!
Nós também podemos utilizá-lo em funções Nós também podemos utilizá-lo em funções sem retorno:sem retorno:
function Updatefunction Update()(): never: never {{
whilewhile (true) { (true) {
consoleconsole.log(.log("Carregando processos!""Carregando processos!"););
}}
}}
Essa função é comum no desenvolvimento de jogos onde nós temosEssa função é comum no desenvolvimento de jogos onde nós temos
um método que funciona como um um método que funciona como um loop infinito, carregando todos osloop infinito, carregando todos os
módulos de um game.módulos de um game.
Agora, qual é Agora, qual é a diferença enta diferença entrere voidvoid ee nevernever ??
O typeO type voidvoid pode receber o valorpode receber o valor nullnull , que indica ausência de um, que indica ausência de um
objeto, ouobjeto, ou undefinedundefined , que indica a , que indica a ausência de qualquer valorausência de qualquer valor..
O typeO type nevernever não pode receber valornão pode receber valor. A s. A seguir você tem umeguir você tem um
exemplo demonstrando os dois types como exemplo demonstrando os dois types como variáveis:variáveis:
Figura 2.1: Type never x void.Figura 2.1: Type never x void.
2.15 Type assertions2.15 Type assertions
OO Type assertionType assertion funciona da mesma forma que ofunciona da mesma forma que o castcast em outrasem outras
linguagens de programação.Com ele, nós podemos alterar o linguagens de programação. Com ele, nós podemos alterar o typetype
de uma variável, sem que o compilador envie uma exception.de uma variável, sem que o compilador envie uma exception.
Para ficar mais claro, imagine o seguinte cenário: você tem umaPara ficar mais claro, imagine o seguinte cenário: você tem uma
função X que recebe um parâmetro do typefunção X que recebe um parâmetro do type anyany , mas, no corpo da, mas, no corpo da
sua função, você tem uma variável do typesua função, você tem uma variável do type numbernumber , que vai receber , que vai receber
esse valor do parâmetro. Como esse valor do parâmetro. Como resolver?resolver?
Com oCom o Type assertionsType assertions é bem simples, basta passar o type que oé bem simples, basta passar o type que o
valor deve receber entre as chavesvalor deve receber entre as chaves <><> . Veja um exemplo. Veja um exemplo
demonstrando esse cenário:demonstrando esse cenário:
function typeAssetionsfunction typeAssetions(codigoAny: any)(codigoAny: any) {{
letlet codigoNumber: codigoNumber: numbernumber = = <<numbernumber>>codigoAny;codigoAny;
return codigoNumber * 10;return codigoNumber * 10;
}}
typeAssetions(10);typeAssetions(10);
Resultado:Resultado:
//number//number
Agora que nós j Agora que nós já conhecemos os types supá conhecemos os types suportados pelo Tortados pelo TypeScript,ypeScript,
no próximo capítulo, vamos avançar para a estrutura de controle eno próximo capítulo, vamos avançar para a estrutura de controle e
repetição.repetição.
C 3C 3
Estruturas de controle e repetiçãoEstruturas de controle e repetição
Podemos pensar em estrutura de controle como um bloco dePodemos pensar em estrutura de controle como um bloco deprogramação que analisa variáveis e escolhe uma programação que analisa variáveis e escolhe uma direção paradireção para
seguir com base nos seguir com base nos parâmetros predefinidoparâmetros predefinidos. A expressãos. A expressão controlecontrole
de fluxode fluxo define bem a sua função, sendo nada mais do que o define bem a sua função, sendo nada mais do que o
processo básico de tomada de decisões. As estruturas de repetiçãoprocesso básico de tomada de decisões. As estruturas de repetição
controlam quantas vezes a instrução deve controlam quantas vezes a instrução deve ser repetida.ser repetida.
Para que essas estruturas fiquem mais claras, vamos a algunsPara que essas estruturas fiquem mais claras, vamos a alguns
exemplos práticos:exemplos práticos:
3.1 if-else3.1 if-else
A instrução A instrução if\elseif\else trabalha com validação de valores booleanos.trabalha com validação de valores booleanos.
Essa é uma das instruções mais utilizadas no momento deEssa é uma das instruções mais utilizadas no momento de
desenvolvimento de um software.desenvolvimento de um software.
letlet condition = true; condition = true;
ifif (condition) (condition)
{{
consoleconsole.log(.log("a variável está com um valor "a variável está com um valor true"true"););
}}
elseelse
{{
consoleconsole.log(.log("a variável está com um valor false""a variável está com um valor false"););
}}
Executando o trecho de código anterior, nós temos o retornoExecutando o trecho de código anterior, nós temos o retorno aa
variável está com um valor truevariável está com um valor true , porque o valor de, porque o valor de conditioncondition éé truetrue
(verdadeiro). Caso o valor de(verdadeiro). Caso o valor de conditioncondition seja alterado paraseja alterado para falsefalse
(falso), o resultado será alterado para(falso), o resultado será alterado para a variável está com um valora variável está com um valor
falsefalse ..
3.2 if-else-if 3.2 if-else-if
Caso seja necessário validar mais de uma condição de umaCaso seja necessário validar mais de uma condição de uma
determinada variável, nós podemos utilizar odeterminada variável, nós podemos utilizar o if\elseif\elseif\elseif\else ..
A seguir A seguir, nós temos um exemplo , nós temos um exemplo com uma variável chamadcom uma variável chamadaa
perfilperfil ..
letlet perfil = perfil = "admin""admin";;
ifif (perfil == (perfil == "superuser""superuser") {) {
consoleconsole.log(.log("Super usuário""Super usuário"););
}}
elseelse ifif (perfil == (perfil == "admin""admin") {) {
consoleconsole.log(.log("Adminitrador""Adminitrador"););
}} elseelse { {
consoleconsole.log(.log("Usuário comum""Usuário comum"););
}}
Note que utilizando a instruçãoNote que utilizando a instrução if\elseif\elseif\elseif\else nós podemos verificar nós podemos verificar
se a variávelse a variável perfilperfil tem o valor inicialtem o valor inicial superusersuperuser ouou adminadmin e, casoe, caso
não tenha nenhum desses dois valores, não tenha nenhum desses dois valores, vai retornar quevai retornar que perfilperfil é deé de
um usuário comum.um usuário comum.
3.3 Operador ternário3.3 Operador ternário
O operador condicional ternário avalia uma expressão booleana eO operador condicional ternário avalia uma expressão booleana e
retorna o resultado de uma das duas expressões, conforme ela éretorna o resultado de uma das duas expressões, conforme ela é
avaliada comoavaliada como truetrue ouou falsefalse ..
A seguir nós temos o A seguir nós temos o mesmo exemplo com uma varmesmo exemplo com uma variável chamadaiável chamada
perfilperfil , só que dessa vez , só que dessa vez utilizando um operador ternáriutilizando um operador ternário parao para
avaliar os perfis:avaliar os perfis:
letlet perfil = perfil = "admin""admin";;
consoleconsole.log(perfil ==.log(perfil == "superuser""superuser" ? ? "Super usuário""Super usuário" : : "Adminitrador""Adminitrador"););
Ou validando os três perfis:Ou validando os três perfis:
letlet perfil = perfil = "admin""admin";;
consoleconsole.log(perfil ==.log(perfil == "superuser""superuser" ? ? "Super usuário""Super usuário" : perfil == : perfil == "admin""admin" ? ?
"Adminitrador""Adminitrador" : : "Usuário comum""Usuário comum"););
3.4 Nullish Coalescing3.4 Nullish Coalescing
Ao T Ao TypeScript 3.7 foi aypeScript 3.7 foi adicionada uma fdicionada uma funcionalidade uncionalidade chamadachamada
Nullish CoalescingNullish Coalescing , que nos permite verificar se um valor é, que nos permite verificar se um valor é nullnull ouou
undefinedundefined utilizando os operadoresutilizando os operadores ???? ..
Essa funcionalidade verifica se o valor Essa funcionalidade verifica se o valor da direita éda direita é nullnull ouou
undefinedundefined e, caso seja, ela retorna o resultae, caso seja, ela retorna o resultado padrão; do padrão; caso não, elacaso não, ela
retorna o valor da direita.retorna o valor da direita.
Para ficar mais claro, vamos criar um exemplo prático com ele.Para ficar mais claro, vamos criar um exemplo prático com ele.
letlet perfil = perfil = "admin""admin";;
letlet perfil = null; perfil = null;
consoleconsole.log(perfil ??.log(perfil ?? 'Usuário comum''Usuário comum'))
consoleconsole.log(perfil ??.log(perfil ?? 'Usuário comum''Usuário comum'))
//Resultado//Resultado
adminadmin
Usuário comumUsuário comum
3.5 switch3.5 switch
A instrução A instrução ifif é indicada para pequenos trechos de código. Casoé indicada para pequenos trechos de código. Caso
seja necessário validar um trecho de seja necessário validar um trecho de código maiorcódigo maior, a instrução, a instrução
switch caseswitch case é a mais indicada.é a mais indicada.
Nela, nós podemos validar mais de um cenário em uma únicaNela, nós podemos validar mais de um cenário em uma única
instrução. Basta passar o valor a ser validado no parâmetro doinstrução. Basta passar o valor a ser validado no parâmetro do
switchswitch e, em cadae, em cada casecase , o valor que se deseja verificar., o valor que se deseja verificar.
A seguir você tem um e A seguir você tem um exemplo de como utixemplo de como utilizar essa instrução:lizar essa instrução:
letlet perfil = perfil = "admin""admin";;switchswitch (perfil) { (perfil) {
casecase "superuser""superuser"::
consoleconsole.log(.log("Super usuário""Super usuário"););
breakbreak;;
casecase "manager""manager"::
consoleconsole.log(.log("Gerente""Gerente"););
breakbreak;;
casecase "admin""admin"::
consoleconsole.log(.log("Adminitrador""Adminitrador"););
breakbreak;;
casecase "user""user"::
consoleconsole.log(.log("Usuário comum""Usuário comum"););
breakbreak;;
defaultdefault::
consoleconsole.log(.log("sem perfil""sem perfil"););
breakbreak;;
}}
O exemplo anterior vai parar no O exemplo anterior vai parar no terceiroterceiro casecase, o, o case admincase admin . Caso. Caso
você mude o valor da variávelvocê mude o valor da variável perfilperfil parapara superusersuperuser , o, o switchswitch
retornaráretornará Super usuárioSuper usuário ..
Caso o valor deCaso o valor de perfilperfil não seja encontrado por nenhum dos cases,não seja encontrado por nenhum dos cases,
ele cairá noele cairá no defaultdefault , então, nesse cenário, o valor, então, nesse cenário, o valor sem perfilsem perfil seráserá
retornado.retornado.
3.6 while3.6 while
A estrutura de re A estrutura de repetiçãopetição whilewhile executa a repetição de um bloco deexecuta a repetição de um bloco de
instruções enquanto uma condição é verdadeira. Ela é instruções enquanto uma condição é verdadeira. Ela é muitomuito
utilizada no desenvolvimento de games.utilizada no desenvolvimento de games.
letlet condicao = true; condicao = true;
whilewhile (condicao) (condicao)
{{
consoleconsole.log(.log('Carregando...''Carregando...'))
}}
3.7 do-while3.7 do-while
OO do/whiledo/while tem quase o mesmo tem quase o mesmo funcionamento da estrutura defuncionamento da estrutura de
repetiçãorepetição whilewhile , com a diferença de que, com o , com a diferença de que, com o uso dele, teremosuso dele, teremos
os comandos executados ao menos uma vez.os comandos executados ao menos uma vez.
letlet condicao = false; condicao = false;
{{
consoleconsole.log(.log('Carregando...''Carregando...'))
}}
whilewhile (condicao); (condicao);
O trecho de código O trecho de código anterior deve retornar o valoranterior deve retornar o valor Carregando...Carregando... umauma
vez. Quando ele entrar na vez. Quando ele entrar na estruturaestrutura whilewhile e validar o valor dae validar o valor da
variável condição que está como false, variável condição que está como false, parará a repetição.parará a repetição.
3.8 for 3.8 for
OO forfor é semelhante aoé semelhante ao whilewhile , pois ele repete o bloco enquanto a, pois ele repete o bloco enquanto a
condição se mantiver verdadeira. A diferença é condição se mantiver verdadeira. A diferença é que nele passamosque nele passamos
um valor inicial e um valor inicial e um valor final para o um valor final para o loop iniciar e terminar.loop iniciar e terminar.
varvar languages = [ languages = ["C#""C#",, "Java""Java",, "JavaScript""JavaScript",, "TypeScript""TypeScript"];];
forfor ( (letlet i = 0; i < languages.length; i++) { i = 0; i < languages.length; i++) {
consoleconsole.log(languages[i]);.log(languages[i]);
}}
Nesse exemplo, oNesse exemplo, o forfor vai incrementar o valor devai incrementar o valor de ii (aumentar),(aumentar),
enquantoenquanto ii for menor que ofor menor que o array languagesarray languages ..
3.9 foreach3.9 foreach
OO foreachforeach é uma simplificação do operadoré uma simplificação do operador forfor para trabalhar empara trabalhar em
coleções de dados. Ele permite acessar coleções de dados. Ele permite acessar cada elementocada elemento
individualmente iterando sobre toda a coleção.individualmente iterando sobre toda a coleção.
varvar languages = [ languages = ["C#""C#",, "Java""Java",, "JavaScript""JavaScript",, "TypeScript""TypeScript"];];
languages.forlanguages.forEach(element => Each(element => {{
consoleconsole.log(element);.log(element);
});});
O exemplo anterior deve retornar todos os O exemplo anterior deve retornar todos os elementos doelementos do arrayarray
languagelanguage sem a necessidade de informação de índices como nosem a necessidade de informação de índices como no forfor ..
Neste capítulo, nós finalizamos a parte básica do livro. No próximo,Neste capítulo, nós finalizamos a parte básica do livro. No próximo,
vamos entrar em um vamos entrar em um paradigma muito utilizado no momento deparadigma muito utilizado no momento de
desenvolvimento de software, a Orientação a Objetos.desenvolvimento de software, a Orientação a Objetos.
C 4C 4
POO (Programação Orientada a Objetos)POO (Programação Orientada a Objetos)
Antes de falar so Antes de falar sobre o paradigma bre o paradigma POO (Programação OriePOO (Programação Orientada antada aObjetos), nós precisamos entender o que é um paradigma e comoObjetos), nós precisamos entender o que é um paradigma e como
isso funciona na prática.isso funciona na prática.
O paradigma de uma linguagem de programação é a suaO paradigma de uma linguagem de programação é a sua
identidade. Ele corresponde a um conjunto de características que,identidade. Ele corresponde a um conjunto de características que,
untas, definem como ela opera e resolve os problemas.untas, definem como ela opera e resolve os problemas.
A Orientação a Ob A Orientação a Objetos é um dos primejetos é um dos primeiros paradigmas quiros paradigmas que nóse nós
aprendemos na faculdade. Caso você analise as aprendemos na faculdade. Caso você analise as principaisprincipais
linguagens da atualidade, a maioria delas possui uma forte baselinguagens da atualidade, a maioria delas possui uma forte base
Orientada a Objetos, o que faz com que o seu aprendizado sejaOrientada a Objetos, o que faz com que o seu aprendizado seja
essencial.essencial.
E como funciona esse E como funciona esse paradigma?paradigma?
Na Orientação a Objetos, nós organizamos o nosso código emNa Orientação a Objetos, nós organizamos o nosso código em
estruturas chamadasestruturas chamadas classesclasses..
Uma classe é uma estrutura que abstrai um conjunto de objetos comUma classe é uma estrutura que abstrai um conjunto de objetos com
características similares. Ela define o comportamento de seuscaracterísticas similares. Ela define o comportamento de seus
objetos através de métodos e os estados possíveis desses objetosobjetos através de métodos e os estados possíveis desses objetosatravés de atributos.através de atributos.
Em outras palavras, uma classe descreve os serviços oferecidos por Em outras palavras, uma classe descreve os serviços oferecidos por
seus objetos e quais informações eles seus objetos e quais informações eles podem armazenarpodem armazenar. Dessa. Dessa
forma, nós podemos ver uma classe como um forma, nós podemos ver uma classe como um molde e este moldemolde e este molde
representa um objeto.representa um objeto.
Um objeto é uma Um objeto é uma representação de algo do mundo real. Para ficar representação de algo do mundo real. Para ficar
mais claro, veja o exemplo a seguir:mais claro, veja o exemplo a seguir:
ObjetoObjeto dodo tipo computador tipo computador
Modelo: G3Modelo: G3
Memória: Memória: 16gb16gb
Tipo: NotebookTipo: Notebook
Marca: DellMarca: Dell
Temos um objeto do tipo computador. Ele contém característicasTemos um objeto do tipo computador. Ele contém características
particulares, como o seu modelo, memória, tipo e sua marca.particulares, como o seu modelo, memória, tipo e sua marca.
Agora pensando Agora pensando em um outro exemplem um outro exemplo:o:
ObjetoObjeto dodo tipo computador tipo computador
Modelo: MacBook airModelo: MacBook air
Memória: 8gbMemória: 8gb
Tipo: NotebookTipo: Notebook
Marca: AppleMarca: Apple
Assim como no exemplo a Assim como no exemplo anteriornterior, temos um objeto d, temos um objeto do tipoo tipo
computadorcomputador. Porém, apesar . Porém, apesar de ambos compartilharem os de ambos compartilharem os mesmosmesmos
atributos, as suas características são atributos,as suas características são diferentes.diferentes.
Podemos dizer que ambos são instâncias da classePodemos dizer que ambos são instâncias da classe ComputadorComputador ..
Agora que nós t Agora que nós temos um exemplo do muemos um exemplo do mundo real, vamos criar ndo real, vamos criar
outros exemplos aplicando o paradigma de Orientação a Objetos.outros exemplos aplicando o paradigma de Orientação a Objetos.
4.1 Classes4.1 Classes
Como esclarecemos, uma classe é um molde com o Como esclarecemos, uma classe é um molde com o qual os objetosqual os objetos
sãosão modeladosmodelados. É nela que passamos quais atributos um objeto. É nela que passamos quais atributos um objeto
deve ter e quais ações ele deve executar.deve ter e quais ações ele deve executar.
Imagine o seguinte cenário: você está mImagine o seguinte cenário: você está modelando um sistema paraodelando um sistema para
um banco e precisa criar uma classe para gerenciamento de contas.um banco e precisa criar uma classe para gerenciamento de contas.
A primeira pergu A primeira pergunta que você deve nta que você deve fazer é: o que tofazer é: o que todas as contasdas as contas
têm? Em uma breve análise, nós logo identificamos: número datêm? Em uma breve análise, nós logo identificamos: número da
conta, nome do titular e conta, nome do titular e saldo, correto?saldo, correto?
Agora que nós t Agora que nós temos alguns dos atriemos alguns dos atributos que toda butos que toda conta tem,conta tem,
vamos à criação da nossa classe.vamos à criação da nossa classe.
No TypeSNo TypeScript, a declaração de uma cript, a declaração de uma classe é feita utilizando aclasse é feita utilizando a
palavra reservadapalavra reservada classclass seguida do nome da classe seguida do nome da classe que queremosque queremos
implementar:implementar:
classclass Conta { Conta {
/* atributos *//* atributos */
}}
Como boa prática, o código da classeComo boa prática, o código da classe ContaConta deve ficar dentro de umdeve ficar dentro de um
arquivo com o mesmo nome da classe, logo a carquivo com o mesmo nome da classe, logo a classelasse ContaConta seráserá
implementada em um arquivo chamadoimplementada em um arquivo chamado conta.tsconta.ts ..
Dentro dessa classe, queremos armazenar as Dentro dessa classe, queremos armazenar as informações queinformações que
descrevem uma conta. Fazemos isso descrevem uma conta. Fazemos isso declarando variáveis dentro dadeclarando variáveis dentro daclasse, que são chamadas de classe, que são chamadas de atributos.atributos.
Seguindo o levantamento que nós fizemos anteriormente, a nossaSeguindo o levantamento que nós fizemos anteriormente, a nossa
classeclasse ContaConta terá número da conta, titular e terá número da conta, titular e saldo.saldo.
classclass Conta { Conta {
numeroDaConta:numeroDaConta: numbernumber;;
titular:titular: stringstring;;
saldo:saldo: numbernumber;;
}}
Para que possamos acessar esses valores, nós precisamos criar umPara que possamos acessar esses valores, nós precisamos criar um
construtor para a nossa classe e atribuir os valores do seuconstrutor para a nossa classe e atribuir os valores do seu
parâmetro aos nossos atributos.parâmetro aos nossos atributos.
Em outras linguagens de programação, nós declaramos o Em outras linguagens de programação, nós declaramos o construtor construtor
com o mesmo nome da classe, mas, com o mesmo nome da classe, mas, no Tno TypeScript, nós utilizamos aypeScript, nós utilizamos a
palavra reservadapalavra reservada constructorconstructor ..
classclass Conta { Conta {
numeroDaConta:numeroDaConta: numbernumber;;
titular:titular: stringstring;;
saldo:saldo: numbernumber;;
constructorconstructor(numeroDaConta: number, titular: string, saldo: number) {(numeroDaConta: number, titular: string, saldo: number) {
thisthis.numeroDaCont.numeroDaConta a = = numeroDaContanumeroDaConta;;
thisthis.titular = titular;.titular = titular;
thisthis.saldo = saldo;.saldo = saldo;
}}
}}
Agora, para cria Agora, para criar um objeto a par um objeto a partir da nossa classe, nrtir da nossa classe, nós precisamosós precisamos
instanciá-la.instanciá-la.
Como em outras linguagens que utilizam o Como em outras linguagens que utilizam o paradigma POO, nósparadigma POO, nós
utilizamos a palavra reservadautilizamos a palavra reservada newnew parapara instanciar/criar instanciar/criar um novo um novo
objeto a partir de uma objeto a partir de uma determinada classe.determinada classe.
/* implementação da class Conta*//* implementação da class Conta*/
constconst primeiraConta = primeiraConta = newnew Conta(1, Conta(1,"Thiago Adriano""Thiago Adriano",1000);,1000);
Feito isso, temos um objeto do tipo conta com os seguintes valores:Feito isso, temos um objeto do tipo conta com os seguintes valores:
número da conta: 1número da conta: 1
titular: Thiago Adrianotitular: Thiago Adriano
saldo inicial: 1000saldo inicial: 1000
4.2 4.2 MM étoétodosdos
Agora que ide Agora que identificamos a nossa classe ntificamos a nossa classe com seus atributos,com seus atributos,
podemos nos perguntar: como podemos manipular esses atributos?podemos nos perguntar: como podemos manipular esses atributos?
É nessa hora que o método entra em cena. Ele é responsável por É nessa hora que o método entra em cena. Ele é responsável por
identificar e executar as operações que a identificar e executar as operações que a classe fornecerá, ou seja,classe fornecerá, ou seja,
quais serviços e ações a classe oferece. Eles são quais serviços e ações a classe oferece. Eles são responsáveis por responsáveis por
definir e realizar um definir e realizar um determinado comportamento.determinado comportamento.
Agora que vimos o Agora que vimos o que é um método que é um método e como ele funcione como ele funciona, vamosa, vamoscriar três novos métodos na nossa classecriar três novos métodos na nossa classe ContaConta : um para : um para adicionar adicionar
saldo, um para sacar do saldo e um outro para consultar o saldo.saldo, um para sacar do saldo e um outro para consultar o saldo.
classclass Conta { Conta {
numeroDaConta:numeroDaConta: numbernumber;;
titular:titular: stringstring;;
saldo:saldo: numbernumber;;
constructorconstructor(numeroDaConta: number, titular: string, saldo: number) {(numeroDaConta: number, titular: string, saldo: number) {
thisthis.numeroDaCont.numeroDaConta a = = numeroDaContanumeroDaConta;;
thisthis.titular = titular;.titular = titular;
thisthis.saldo = saldo;.saldo = saldo;
}}
consultaSaldo():consultaSaldo(): stringstring { {
returnreturn `O seu saldo atual é: ${ `O seu saldo atual é: ${thisthis.saldo}`;.saldo}`;
}}
adicionaSaldo(saldo:adicionaSaldo(saldo: numbernumber):): voidvoid { {
thisthis.saldo + saldo;.saldo + saldo;
}}
sacarDoSaldo(valor:sacarDoSaldo(valor: numbernumber):): voidvoid { {
thisthis.saldo -= valor;.saldo -= valor;
}}
}}
4.3 M4.3 Modifiodificadorcadores de acesses de acessoo
Com a evolução da nossa classeCom a evolução da nossa classe ContaConta , acabamos deixando uma, acabamos deixando uma
brecha de segurança: ela nos permite alterar o valor do saldo dabrecha de segurança: ela nos permite alterar o valor do saldo da
conta chamando a propriedade saldo direto. O correto seria conta chamando a propriedade saldo direto. O correto seria alterar alterar
esse atributo somente através dos métodosesse atributo somente através dos métodos adicionaSaldoadicionaSaldo ee
sacarDoSaldosacarDoSaldo ..
Para resolver esse problema, nós podemos Para resolver esse problema, nós podemos utilizar um modificador utilizar um modificador
de acesso. Caso esse seja o seu de acesso. Caso esse seja o seu primeiro contato com ele, segueprimeiro contato com ele, segue
uma lista com os modificadores de acesso suportados pelouma lista com os modificadores de acesso suportados pelo
TypeScript:TypeScript:
Public Public : é o : é o modificador padrão. Tmodificador padrão. Tudo o que for udo o que for declarado semdeclarado semum modificador de acesso automaticamente se torna um modificador de acesso automaticamente se torna público.público.
PrivatePrivate: com este : com este modificadormodificador, estamos dizendo que esse, estamos dizendo que esse
atributo ou método não pode ser acessado de fora da classeatributo ou método não pode ser acessado de fora da classe
em que ele foi em que ele foi declarado.declarado.
Protected Protected : é bem parecido com o: é bem parecido com o privateprivate , a diferença entre, a diferença entre
eles é que oeles é que o protectedprotected pode ser acessado de uma classe quepode ser acessado de uma classe que
herda de uma outra classe.herda de uma outra classe.
Agora que já Agora que já conhecemos os modificadconhecemos os modificadores de acesso, vamosores de acesso, vamos
corrigir a brecha de segurança do corrigir a brecha de segurança do nosso código alterando o atributonosso código alterando o atributo
saldosaldo parapara privateprivate ..
exportexport classclass Conta { Conta {
/* outros atributos*//* outros atributos*/
privateprivate saldo: saldo: numbernumber;;
/* outros métodos*//* outros métodos*/
Dessa forma, conseguimos garantir que o Dessa forma, conseguimos garantir que o saldo de um determinadosaldo de um determinado
cliente seja alterado somente através dos métodoscliente seja alterado somente através dos métodos adicionaSaldoadicionaSaldo ee
sacarDoSaldosacarDoSaldo ..
4.4 Herança4.4 Herança
Um dos pilares da POO é a Um dos pilares da POO é a herança, ela nos permite reutilizar herança, ela nos permite reutilizar
código sem a necessidade de código sem a necessidade de duplicá-lo.duplicá-lo.
Imagine o seguinte cenário: chegou uma nova demanda paraImagine o seguinte cenário: chegou uma nova demanda para
criarmos dois novos tipos de contas, com alguns atributoscriarmos dois novos tipos de contas, com alguns atributos
diferentes, uma conta para pessoa física e uma outra para pessoadiferentes, uma conta para pessoa física e uma outra para pessoa
urídica.urídica.
Uma solução seria copiar a mesma classe, mudando seu nome eUma solução seria copiar a mesma classe, mudando seu nome e
alguns atributos, mas assim nós alguns atributos, mas assim nós estaríamos duplicando o nossoestaríamos duplicando o nosso
código, o que o código, o que o tornaria mais complexo e sua tornaria mais complexo e sua manutenção, árdua.manutenção, árdua.
Então como resolver essa demanda sem duplicarmos o nossoEntão como resolver essa demanda sem duplicarmos o nosso
código?código?
Neste momento, entramos em um dos pilares da POO, a herança.Neste momento, entramos em um dos pilares da POO, a herança.
A herança otimiza A herança otimiza a produção da na produção da nossa aplicação em tempossa aplicação em tempo eo e
linhas de código. Através dela, podemos herdar os métodos e oslinhas de código. Através dela, podemos herdar os métodos e os
atributos de uma outra classe.atributos de uma outra classe.
Para ficar mais claro, Para ficar mais claro, vamos a um vamos a um exemplo prático. No TypeScriptexemplo prático. No TypeScript,,
para herdarmos uma classe, nós para herdarmos uma classe, nós utilizamos a palavra reservadautilizamos a palavra reservada
extendsextends ..
classclass ContaPF extends Conta {} ContaPF extends Conta {}
classclass ContaPJ extends Conta {} ContaPJ extends Conta {}
Caso você instancie essas classes, note que elas herdarão todas asCaso você instancie essas classes, note que elas herdarão todas as
funcionalidadfuncionalidades da es da classe paiclasse pai ContaConta ..
classclass ContaPF extends Conta {} ContaPF extends Conta {}
classclass ContaPJ extends Conta {} ContaPJ extends Conta {}
constconst pessoaFisica = pessoaFisica = newnew ContaPF(1, ContaPF(1, "Thiago Adriano""Thiago Adriano", 1000);, 1000);
constconst pessoaJuridica = pessoaJuridica = newnew ContaPJ(1, ContaPJ(1, "Thiago Adriano""Thiago Adriano", 1000);, 1000);
Bem mais simples do que duplicar o código, não é?Bem mais simples do que duplicar o código, não é?
Agora, voltando Agora, voltando à descrição da nossa à descrição da nossa demanda, essas duademanda, essas duas novass novas
classes têm atributos distintos. Para pessoa física, nós devemosclasses têm atributos distintos. Para pessoa física, nós devemos
adicionar o atributo CPF e, para pessoa adicionar o atributo CPF e, para pessoa jurídica, o CNPJ.jurídica, o CNPJ.
VVamos adicionar essas amos adicionar essas novas propriedades começando por PFnovas propriedades começando por PF
(pessoa física):(pessoa física):
classclass ContaPF extends Conta { ContaPF extends Conta {
cpf:cpf: numbernumber;;
constructorconstructor(cpf: number, numeroDaConta: number, titular: (cpf: number, numeroDaConta: number, titular: string,string,
saldo: number) {saldo: number) {
supersuper(numeroDaConta, titular, saldo);(numeroDaConta, titular, saldo);
thisthis.cpf = cpf;.cpf = cpf;
}}
}}
Note que nós temos uma nova palavra no nosso código, a palavraNote que nós temos uma nova palavra no nosso código, a palavra
reservadareservada supersuper . Ela é utilizada para passar os valores que estamos. Ela é utilizada para passar os valores que estamos
recebendo via construtor para o construtor da recebendo via construtor para o construtor da nossa classe pai.nossa classe pai.
Agora, adiciona Agora, adicionando o atributo ndo o atributo CNPJ na conta PJ (peCNPJ na conta PJ (pessoa jurídica):ssoa jurídica):
classclass ContaPJ extends Conta { ContaPJ extends Conta {
cnpj:cnpj: numbernumber;;
constructorconstructor(cnpj: number, numeroDaConta: number, titular: (cnpj: number, numeroDaConta: number, titular: string,string,
saldo: number) {saldo: number) {
supersuper(numeroDaConta, titular, saldo);(numeroDaConta, titular, saldo);
thisthis.cnpj = cnpj;.cnpj = cnpj;
}}
}}
constconst pessoaJuridica = pessoaJuridica = newnew ContaPJ(46173051000116, 1, ContaPJ(46173051000116, 1, "Thiago Adriano""Thiago Adriano",,
1000);1000);
Com essa implementação, nós conseguimos finalizar a Com essa implementação, nós conseguimos finalizar a nossanossa
demanda, correto? Sim, mas o nosso código está redondinho?demanda, correto? Sim, mas o nosso código está redondinho? Ainda não. Ainda não.
Analisando a cla Analisando a classesse ContaConta , note que podemos melhorar alguns, note que podemos melhorar alguns
pontos, como:pontos, como:
O número da conta deve ser gerado pela classe e não passadoO número da conta deve ser gerado pela classe e não passado
para ela no momento de criação de um novo objeto.para ela no momento de criação de um novo objeto.
O métodoO método sacarsacar deve ter alguma validação de saldo.deve ter alguma validação de saldo.
Os métodos da classe pai devem ficar protegidos para queOs métodos da classe pai devem ficar protegidos para que
somente as classes filhas possam acessá-los.somente as classes filhas possam acessá-los.
Veja a implementação de cada melhoria a seguir:Veja a implementação de cada melhoria a seguir:
Geração do número da contaGeração do número da conta
O primeiro passo será deixar o atributoO primeiro passo será deixar o atributo numeroDaContanumeroDaConta comocomo privateprivate ,,
dessa forma ele fica restrito ao escopo da classedessa forma ele fica restrito ao escopo da classe ContaConta ..
exportexport classclass Conta { Conta {
privateprivate numeroDaContanumeroDaConta:: numbernumber;;
Feito isso, vamos criar um algoritmo para a geração de númerosFeito isso, vamos criar um algoritmo para a geração de números
aleatórios para que possamos remover o aleatórios para que possamos remover o parâmetroparâmetro numeroDaContanumeroDaConta
do construtor da classedo construtor da classe ContaConta ::
constructorconstructor(titular: string, saldo: number) {(titular: string, saldo: number) {
thisthis.numeroDaCont.numeroDaConta a == MathMath.floor(.floor(MathMath.random() * 1000) + 1;.random() * 1000) + 1;
Observação: esse trecho de Observação: esse trecho de código é somente para ilustrar acódigo é somentepara ilustrar a
criação de um número aleatório. Em cenários de produção, nóscriação de um número aleatório. Em cenários de produção, nós
devemos utilizar algo mais elaborado.devemos utilizar algo mais elaborado.
Protegendo os métodos da classe paiProtegendo os métodos da classe pai
Para proteger os métodos da classe pai, basta adicionarmos aPara proteger os métodos da classe pai, basta adicionarmos a
palavra reservadapalavra reservada protectedprotected , que vimos , que vimos anteriormente, na frente deanteriormente, na frente de
cada método:cada método:
exportexport classclass Conta { Conta {
privateprivate numeroDaContanumeroDaConta:: numbernumber;;
titular:titular: stringstring;;
privateprivate saldo: saldo: numbernumber;;
constructorconstructor(titular: string, saldo: number) {(titular: string, saldo: number) {
thisthis.numeroDaCont.numeroDaConta a == MathMath.floor(.floor(MathMath.random() * 1000) + 1;.random() * 1000) + 1;
thisthis.titular = titular;.titular = titular;
thisthis.saldo = saldo;.saldo = saldo;
}}
protected protected consultaSaldo(consultaSaldo():): stringstring { {
returnreturn `O seu saldo atual é: ${ `O seu saldo atual é: ${thisthis.saldo}`;.saldo}`;
}}
protected protected adicionaSaldo(adicionaSaldo(saldo:saldo: numbernumber):): voidvoid { {
thisthis.saldo + saldo;.saldo + saldo;
}}
protected protected sacarDoSaldo(vsacarDoSaldo(valor:alor: numbernumber):): voidvoid { {
thisthis.saldo -= valor;.saldo -= valor;
}}
}}
Com isso, nós conseguimos garantir que os métodosCom isso, nós conseguimos garantir que os métodos consultaSaldoconsultaSaldo ,,
adicionaSaldoadicionaSaldo ee sacarDoSaldosacarDoSaldo sejam acessados somente pela própriasejam acessados somente pela própria
classeclasse ContaConta e pelas classes que a herdarem.e pelas classes que a herdarem.
VValidando o método de saqalidando o método de saqueue
Analisando os ti Analisando os tipos de conta PF e pos de conta PF e PJ, notamos que elPJ, notamos que eles têm umaes têm uma
política diferente no momento do saque. Os política diferente no momento do saque. Os clientes que sãoclientes que são
cadastrados como PF não podem ficar negativos, mas os clientescadastrados como PF não podem ficar negativos, mas os clientes
PJ podem. Como resolver essa demanda?PJ podem. Como resolver essa demanda?
O primeiro passo será alterar o métodoO primeiro passo será alterar o método consultaSaldoconsultaSaldo na classena classe
ContaConta , para que ele retorne um número e não uma string com o, para que ele retorne um número e não uma string com o
valor atual do saldo da valor atual do saldo da conta.conta.
/* implementação da classe Conta*//* implementação da classe Conta*/
protected protected consultaSaldo(consultaSaldo():): numbernumber { {
returnreturn thisthis.saldo;.saldo;
}}
/* outros métodos*//* outros métodos*/
O próximo passo será sobrescrever o métodoO próximo passo será sobrescrever o método consultaSaldoconsultaSaldo nasnas
classesclasses ContaPFContaPF ee ContaPJContaPJ ::
//PF//PF
classclass ContaPF extends Conta { ContaPF extends Conta {
/* implementação da classe Conta*//* implementação da classe Conta*/
consultar(): consultar(): stringstring { {
returnreturn `Saldo atual: ${ `Saldo atual: ${thisthis.consultaSaldo()}`;.consultaSaldo()}`;
}}
/* outros métodos*//* outros métodos*/
}}
//PJ//PJ
classclass ContaPJ extends Conta { ContaPJ extends Conta {
/* implementação da classe Conta*//* implementação da classe Conta*/
consultar():consultar(): stringstring { {
returnreturn `Saldo atual: ${ `Saldo atual: ${thisthis.consultaSaldo()}`;.consultaSaldo()}`;
}}
/* outros métodos*//* outros métodos*/
}}
Agora vamos atuali Agora vamos atualizar o métodozar o método sacarDoSaldosacarDoSaldo conforme a demandaconforme a demanda
que temos começando por PF, onde o cliente não pode ficar que temos começando por PF, onde o cliente não pode ficar
negativo. Vnegativo. Vamos criar um amos criar um novo método chamadonovo método chamado sacarsacar , que valide, que valide
se o valor do saque é maior que o se o valor do saque é maior que o saldo da conta e se o saldo ésaldo da conta e se o saldo é
maior que 0, e adicionar um trecho de código a ele.maior que 0, e adicionar um trecho de código a ele.
classclass ContaPF extends Conta { ContaPF extends Conta {
/* implementação da classe Conta*//* implementação da classe Conta*/
sacar(valor:sacar(valor: numbernumber) {) {
ifif ( (thisthis.consultaSaldo.consultaSaldo() > 0 () > 0 && valor <=&& valor <= thisthis.consultaSaldo.consultaSaldo()) ()) {{
thisthis.sacarDoSaldo(valor);.sacarDoSaldo(valor);
}}
}}
/* outros métodos*//* outros métodos*/
}}
Como na conta PJ o cliente pode ficar negativo, vamos criar umComo na conta PJ o cliente pode ficar negativo, vamos criar um
método chamadométodo chamado sacarsacar e passar o valor do seu parâmetro para oe passar o valor do seu parâmetro para o
métodométodo sacarDoSaldosacarDoSaldo da classe pai.da classe pai.
classclass ContaPJ extends Conta { ContaPJ extends Conta {
/* implementação da classe Conta*//* implementação da classe Conta*/
sacar(valor: sacar(valor: numbernumber) {) {
thisthis.sacarDoSaldo(valor);.sacarDoSaldo(valor);
}}
/* outros métodos*//* outros métodos*/
}}
Agora a nossa solu Agora a nossa solução está quase ficanção está quase ficando redondinhado redondinha. Caso você. Caso você
analise novamente a classeanalise novamente a classe ContaConta , note que depois das nossas, note que depois das nossas
alterações nós não conseguimos mais pegar o número de umaalterações nós não conseguimos mais pegar o número de uma
conta. Na próxima seção, veremos uma forma de resolver esseconta. Na próxima seção, veremos uma forma de resolver esse
problema.problema.
4.5 G4.5 Getteetters & Settersrs & Setters
O TypeScripO TypeScript tem suporte aos t tem suporte aos getters/setters. Caso esse seja o getters/setters. Caso esse seja o seuseu
primeiro contato com eles, a seguir você tem uma breve descriçãoprimeiro contato com eles, a seguir você tem uma breve descrição
sobre cada um:sobre cada um:
getter getter : esse método é utilizado quando queremos acessar o: esse método é utilizado quando queremos acessar o
valor de uma propriedade de um valor de uma propriedade de um objeto.objeto.
setter setter : esse método é : esse método é utilizado quando queremos alterar o valor utilizado quando queremos alterar o valor
de uma propriedade de um objeto.de uma propriedade de um objeto.
VVoltando ao nosso código, como podemos saber oltando ao nosso código, como podemos saber qual é o número qual é o número dadaconta de um cliente agora conta de um cliente agora que o atributoque o atributo _numeroDaConta_numeroDaConta está comoestá como
privado?privado?
Para resolver esse problema, nós podemos Para resolver esse problema, nós podemos utilizar outilizar o gettergetter ::
classclass Conta { Conta {
privateprivate _numeroDaCont_numeroDaConta:a: numbernumber;;
titular:titular: stringstring;;
privateprivate _saldo: _saldo: numbernumber;;
getget numeroDaConta()numeroDaConta():: numbernumber { {
returnreturn thisthis._numeroDaConta;._numeroDaConta;
}}
/* outros métodos*//* outros métodos*/
Note que foram adicionados dois underlines, um antes Note que foram adicionados dois underlines, um antes dede
numeroDaContanumeroDaConta e outro antes dee outro antes de saldosaldo . Essa é uma das . Essa é uma das convençõesconvenções
utilizadas pela comunidade de desenvolvedores JavaScript utilizadas pela comunidade de desenvolvedores JavaScript paraparainformar que um atributo é privado.informar que um atributo é privado.
Agora para consu Agora para consultar o número da ltar o número da conta de um clientconta de um cliente PF ou PJe PF ou PJ
ficou bem simples, basta chamar ficou bem simples, basta chamar a propriedadea propriedade numeroDaContanumeroDaConta ..
Pegando a instância de uma conta PJ como exemplo, nós temos:Pegando a instânciade uma conta PJ como exemplo, nós temos:
constconst pessoaJuridica = pessoaJuridica = newnew ContaPJ(4617305ContaPJ(46173051000116,1000116, "Thiago Adriano""Thiago Adriano",,
1000);1000);
consoleconsole.log(pessoaJuridica.numeroDaConta);.log(pessoaJuridica.numeroDaConta);
4.6 Classe abstrata4.6 Classe abstrata
Analisando o n Analisando o nosso código novamentosso código novamente, podemos observar e, podemos observar que aque a
nossa classenossa classe ContaConta está servindo de modelo para as classesestá servindo de modelo para as classes
ClientePFClientePF ee ClientePJClientePJ . Agora todo novo cliente será criado a partir . Agora todo novo cliente será criado a partir
dessas duas novas classes. Aqui chegamos a mais um dessas duas novas classes. Aqui chegamos a mais um conceito daconceito da
POO, as classes abstratas.POO, as classes abstratas.
As classes abstratas não As classes abstratas não permitem realizar qpermitem realizar qualquer tipo dualquer tipo dee
instância, elas são utilizadas como modelos para instância, elas são utilizadas como modelos para outras classes,outras classes,
que são conhecidas como classes concretas.que são conhecidas como classes concretas.
Para tornar a nossa classePara tornar a nossa classe ContaConta abstrata, basta adicionar a palavraabstrata, basta adicionar a palavra
reservadareservada abstractabstract na frente da palavra reservadana frente da palavra reservada classclass ::
abstractabstract classclass Conta { Conta {
/* implementação da classe*//* implementação da classe*/
}}
Agora a nossa classe Agora a nossa classe ContaConta será somente um modelo para criaçãoserá somente um modelo para criação
de outras classes, ela não pode mais ser instanciada. Caso vocêde outras classes, ela não pode mais ser instanciada. Caso você
tente instanciá-la, deve receber o seguinte erro:tente instanciá-la, deve receber o seguinte erro:
Figura 4.1: Erro ao tentar instanciar classe abstrata.Figura 4.1: Erro ao tentar instanciar classe abstrata.
4.7 Readonly4.7 Readonly
Analisando os p Analisando os possíveis ajustes na classeossíveis ajustes na classe ContaConta , notamos que está, notamos que está
faltando mais um ponto. O faltando mais um ponto. O atributoatributo _numeroDaConta_numeroDaConta está comoestá como
privado, mas nada impede que algum outro privado, mas nada impede que algum outro método dentro damétodo dentro da
própria classe altere o seu própria classe altere o seu valorvalor. Como resolver esse . Como resolver esse problema?problema?
Para isso, temos a palavra reservadaPara isso, temos a palavra reservada readonlyreadonly , que faz com que, que faz com que
uma propriedade dentro de uma classe seja uma propriedade dentro de uma classe seja utilizada como somenteutilizada como somente
leitura. Uma vez setado o leitura. Uma vez setado o seu valorseu valor, ele não pode , ele não pode ser alterado.ser alterado.
abstractabstract classclass Conta { Conta {
privateprivate readonly readonly _numeroDaConta_numeroDaConta:: numbernumber;;
/* implementação da classe*//* implementação da classe*/
}}
Dessa forma, nós conseguimos garantir que a Dessa forma, nós conseguimos garantir que a propriedadepropriedade
_numeroDaConta_numeroDaConta não seja alterada por nenhum outro método denão seja alterada por nenhum outro método de
dentro da classedentro da classe ContaConta ..
Com isso, nós finalizamos mais este capítulo. No próximo, veremosCom isso, nós finalizamos mais este capítulo. No próximo, veremos
como trabalhar com como trabalhar com as interfaces no as interfaces no TTypeScript.ypeScript.
C 5C 5
InterfacesInterfaces
5.1 Introdução a interfaces5.1 Introdução a interfaces
Além dos tipos pri Além dos tipos primitivos, que abordamitivos, que abordamos no segundo capmos no segundo capítuloítulo
deste livro, o TypeScript permite que tipos complexos, como funçõesdeste livro, o TypeScript permite que tipos complexos, como funções
e objetos sejam definidos e usados como restrições de tipo. Assime objetos sejam definidos e usados como restrições de tipo. Assim
como os objetos literais são a raiz da definição de objeto emcomo os objetos literais são a raiz da definição de objeto em
JavaScript, os tipos literais de objeto são as definições de um tipoJavaScript, os tipos literais de objeto são as definições de um tipo
de objeto no TypeScriptde objeto no TypeScript. Em s. Em sua forma mais básica, parece ua forma mais básica, parece muitomuito
com um objeto literal do com um objeto literal do JavaScript.JavaScript.
A seguir A seguir, você tem um exemplo d, você tem um exemplo demonstrando a deemonstrando a definição de umafinição de uma
variável chamadavariável chamada pessoapessoa , que aceita qualquer objeto com , que aceita qualquer objeto com asas
propriedadespropriedades nomenome ,, idadeidade ,, emailemail ee telefonetelefone ..
letlet pessoa: { pessoa: {
nome:nome: stringstring;;
idade:idade: numbernumber;;
email:email: stringstring;;
telefone:telefone: numbernumber;;
};};
Note que, diferente de um objeto literal do Note que, diferente de um objeto literal do JavaScript, o tipo literalJavaScript, o tipo literal
de objeto separa os campos usando ponto e vírgula, e não vírgulas.de objeto separa os campos usando ponto e vírgula, e não vírgulas.
Quando o TypeScriQuando o TypeScript compara dois tipos pt compara dois tipos de objeto para decidir sede objeto para decidir se
eles correspondem ou não, isso é eles correspondem ou não, isso é feito estruturalmente. Issofeito estruturalmente. Isso
significa que, em vez de comparar os tipos verificando se os doissignifica que, em vez de comparar os tipos verificando se os dois
herdam o mesmo objeto de restrição de base, ele compara asherdam o mesmo objeto de restrição de base, ele compara as
propriedades de cada um dos objetos.propriedades de cada um dos objetos.
Caso um objeto tenha todas as Caso um objeto tenha todas as propriedades que foram definidas nopropriedades que foram definidas no
momento da definição da variável, elas são momento da definição da variável, elas são consideradasconsideradas
compatíveis. Para ficar mais claro, vamos a um exemplo prático:compatíveis. Para ficar mais claro, vamos a um exemplo prático:
letlet pessoa: { nome: pessoa: { nome: stringstring; idade:; idade: numbernumber; email:; email: stringstring; telefone:; telefone:
numbernumber;};;};
pessoa = { nome:pessoa = { nome: 'Bill''Bill', idade:, idade: '63''63', email:, email: 'bill@gmail.com''bill@gmail.com', telefone:, telefone:
555555555};555555555};
// Compatíveis, pois esse objeto // Compatíveis, pois esse objeto contém as mesmas propriedadescontém as mesmas propriedades
Caso alguma das propriedades não seja compatível ou Caso alguma das propriedades não seja compatível ou uma de suasuma de suas
definições esteja faltando, o tipo de objeto será considerado nãodefinições esteja faltando, o tipo de objeto será considerado não
compatível e o compilador deve gerar um dos erros a seguir:compatível e o compilador deve gerar um dos erros a seguir:
pessoa = { nome:pessoa = { nome: 'Bill''Bill', idade: 63, email:, idade: 63, email: 'bill@gmail.com''bill@gmail.com', telefone:, telefone:
'555555555''555555555' }; };
/*/*
Erro: (property) telefone: numberErro: (property) telefone: number
Type 'string' is not Type 'string' is not assignable to type 'number'.ts(2322)assignable to type 'number'.ts(2322)
The expected type comes from property 'telefone' which is declared here onThe expected type comes from property 'telefone' which is declared here on
type '{ nome: string; idade: number; email: string; telefone: number; }'type '{ nome: string; idade: number; email: string; telefone: number; }'
*/*/
pessoa = { nome:pessoa = { nome: 'Bill''Bill', idade: 63, email:, idade: 63, email: 'bill@gmail.com''bill@gmail.com' }; };
/*/*
Erro: Property 'telefone' is missing in type '{ nome: string; idade:Erro: Property 'telefone' is missing in type '{ nome: string;idade:
number; email: string; }' but required in type '{ number; email: string; }' but required in type '{ nome: string; idade:nome: string; idade:
number; email: string; telefone: number; email: string; telefone: number; }'.ts(2741)number; }'.ts(2741)
'telefone' is declared here.'telefone' is declared here.
*/*/
pessoa = { nome:pessoa = { nome: 'Bill''Bill', idade: 63, email:, idade: 63, email: 'bill@gmail.com''bill@gmail.com', telefone:, telefone:
555555555, endereco:555555555, endereco: 'rua x''rua x' }; };
/*/*
Erro: Type '{ Erro: Type '{ nome: string; idade: number; email: string; nome: string; idade: number; email: string; telefone:telefone:
number; endereco: string; }' is not assignable to type '{ number; endereco: string; }' is not assignable to type '{ nome: string;nome: string;
idade: number; email: string; telefone: number; }'.idade: number; email: string; telefone: number; }'.
Object literal may only Object literal may only specify known properties, and 'endereco' doesspecify known properties, and 'endereco' does
not exist in type '{ not exist in type '{ nome: string; idade: number; email: string; telefone:nome: string; idade: number; email: string; telefone:
number; }'.ts(2322)number; }'.ts(2322)
*/*/
Agora que nós j Agora que nós já entendemos como fá entendemos como funciona um tipo unciona um tipo de objeto ede objeto e
aprendemos a trabalhar com POO, como aprendemos a trabalhar com POO, como definir outra variável comdefinir outra variável com
os mesmos tipos que foram definidos paraos mesmos tipos que foram definidos para pessoapessoa sem repetir todassem repetir todas
as propriedades?as propriedades?
//Exemplo duplicando as propriedades//Exemplo duplicando as propriedades
letlet pessoa: { nome: pessoa: { nome: stringstring; idade:; idade: numbernumber; email:; email: stringstring; telefone:; telefone:
numbernumber;};;};
letlet pessoa2: { nome: pessoa2: { nome: stringstring; idade:; idade: numbernumber; email:; email: stringstring; telefone:; telefone:
numbernumber;};;};
Uma solução seria utilizar o Uma solução seria utilizar o operadoroperador typeoftypeof para definir umapara definir uma
restrição de tipo.restrição de tipo.
letlet pessoa: { nome: pessoa: { nome: stringstring; idade:; idade: numbernumber; email:; email: stringstring; telefone:; telefone:
numbernumber;};;};
letlet pessoa2: pessoa2: typeoftypeof pessoa; pessoa;
Esse mecanismo ajuda a Esse mecanismo ajuda a reduzir a quantidade de código necessárioreduzir a quantidade de código necessário
para fazer referências ao mesmo tipo, mas existe outra abstraçãopara fazer referências ao mesmo tipo, mas existe outra abstração
ainda mais poderosa no TypeScript para a reutilização de tipos deainda mais poderosa no TypeScript para a reutilização de tipos de
objetos: asobjetos: as interfacesinterfaces..
A interface é a A interface é a essência de um tipo essência de um tipo literal de objliteral de objeto. Ela é umeto. Ela é um
conjunto de métodos e propriedades que descrevem um conjunto de métodos e propriedades que descrevem um objeto,objeto,
porém não inicializa nem os porém não inicializa nem os implementa. Para ficar mais claro,implementa. Para ficar mais claro,
vamos alterar o exemplo anterior utilizando uma interface:vamos alterar o exemplo anterior utilizando uma interface:
interfaceinterface Pessoa { Pessoa {
nome:nome: stringstring;;
idade:idade: numbernumber;;
email:email: stringstring;;
telefone:telefone: numbernumber;;
}}
Agora nós pode Agora nós podemos passar o tipomos passar o tipo PessoaPessoa para outras variáveis sempara outras variáveis sem
a necessidade de duplicar as sa necessidade de duplicar as suas propriedades:uas propriedades:
letlet pessoa: Pessoa; pessoa: Pessoa;
letlet pessoa2: Pessoa; pessoa2: Pessoa;
Como você pode observar no Como você pode observar no exemplo anteriorexemplo anterior, essa alteração, essa alteração
permite que o tipopermite que o tipo PessoaPessoa seja utilizado em vários locais dentro doseja utilizado em vários locais dentro do
código sem a necessidade de código sem a necessidade de redefinir seus detalhes repetidasredefinir seus detalhes repetidas
vezes. As interfaces também vezes. As interfaces também podem estender outras interfaces oupodem estender outras interfaces ou
classes usando a palavra reservadaclasses usando a palavra reservada extendsextends para compor tipos maispara compor tipos mais
complexos a partir de tipos simples:complexos a partir de tipos simples:
interfaceinterface PessoaJuridica extends Pessoa { PessoaJuridica extends Pessoa {
conta:conta: numbernumber;;
cnpj:cnpj: numbernumber;;
}}
Neste exemplo, o tipoNeste exemplo, o tipo PessoaJuridicaPessoaJuridica estende as propriedadesestende as propriedades nomenome ,,
idadeidade ,, emailemail ee telefonetelefone da interfaceda interface IPessoaIPessoa , e declara duas novas, e declara duas novas
propriedades:propriedades: contaconta ee cnpjcnpj ..
As propriedades p As propriedades podem ser especificadaodem ser especificadas como opcionais e, ps como opcionais e, paraara
isso, basta adicionar o operadorisso, basta adicionar o operador ?? ao final do nome da ao final do nome da propriedade:propriedade:
interfaceinterface IPessoa { IPessoa {
nome:nome: stringstring;;
idade:idade: numbernumber;;
email:email: stringstring;;
telefone?:telefone?: numbernumber;;
}}
Até agora só criamo Até agora só criamos tipos de objeto s tipos de objeto com propriedadecom propriedades, mas nãos, mas não
demonstramos como adicionar um método a um objeto. Odemonstramos como adicionar um método a um objeto. O
TTypeScript nos fornece ypeScript nos fornece uma sintaxe abreviada para uma sintaxe abreviada para especificá-los:especificá-los:
interfaceinterface PessoaJuridica extends Pessoa { PessoaJuridica extends Pessoa {
conta:conta: numbernumber;;
cnpj:cnpj: numbernumber;;
abrirConta():abrirConta(): booleanboolean;;
}}
Nesse exemplo, adicionamos o métodoNesse exemplo, adicionamos o método abrirConta()abrirConta() na interfacena interface
PessoaJuridicaPessoaJuridica , que não aceita argumento e , que não aceita argumento e retorna umretorna um booleanboolean ..
Como as propriedades, os métodos também Como as propriedades, os métodos também podem ser opcionais,podem ser opcionais,
basta adicionar o operadorbasta adicionar o operador ?? depois do nome do método:depois do nome do método:
interfaceinterface PessoaJuridica extends Pessoa { PessoaJuridica extends Pessoa {
conta:conta: numbernumber;;
cnpj:cnpj: numbernumber;;
abrirConta?():abrirConta?(): booleanboolean;;
}}
Voltando a uma das versões anteriores do TypeScript, a 2.7, a elaVoltando a uma das versões anteriores do TypeScript, a 2.7, a ela
foi adicionada a funcionalidade de ter propriedades de nomes, comofoi adicionada a funcionalidade de ter propriedades de nomes, como
constantesconstantes, nos tipos. Isso significa que as interfaces podem ser , nos tipos. Isso significa que as interfaces podem ser
definidas por strings, constantes, números ou literais.definidas por strings, constantes, números ou literais.
constconst example1 = example1 = 'string''string';;
constconst example2 = Symbol(); example2 = Symbol();
interfaceinterface MeuExemplo { MeuExemplo {
[example1]:[example1]: stringstring;;
[example2]:[example2]: booleanboolean;;
}}
Avançando na implementação de uma interfaceAvançando na implementação de uma interface
Bom, até aqui vimos o básico sobre a implementação de umaBom, até aqui vimos o básico sobre a implementação de uma
interface, abordamos o que ela é interface, abordamos o que ela é e como trabalhar com ela e como trabalhar com ela tipandotipando
um objeto.um objeto.
Agora avançando Agora avançando nesse assunto, vamos a unesse assunto, vamos a uma das formas maisma das formas mais
comuns de se trabalhar com uma interface na Orientação a Objetos:comuns de se trabalhar com uma interface na Orientação a Objetos:
sua implementação poruma classe.sua implementação por uma classe.
Quando uma interface é implementada por uma classe, ela é vistaQuando uma interface é implementada por uma classe, ela é vista
como um contrato, obrigando a classe como um contrato, obrigando a classe que a está implementando aque a está implementando a
definir todos os seus mdefinir todos os seus métodos e propriedades.étodos e propriedades.
Para que você possa ter um melhor entendimento sobre essePara que você possa ter um melhor entendimento sobre esse
assunto, vamos a um assunto, vamos a um exemplo prático utilizando a linha de raciocínioexemplo prático utilizando a linha de raciocínio
do capítulo anterior sobre POO. Para issdo capítulo anterior sobre POO. Para isso, imagine o seguinteo, imagine o seguinte
cenário: agora todas as nossas contas PJ e PF precisam ter cenário: agora todas as nossas contas PJ e PF precisam ter umum
método que calcule o seu método que calcule o seu valor tributário anual. Como fazer comvalor tributário anual. Como fazer com
que os dois tipos de conta implementem esse método?que os dois tipos de conta implementem esse método?
Utilizando uma interface é bem simples, nós só precisamos definir oUtilizando uma interface é bem simples, nós só precisamos definir o
nosso contrato, como no exemplo a seguir:nosso contrato, como no exemplo a seguir:
interfaceinterface Tributavel { Tributavel {
CalculaTributo():CalculaTributo(): numbernumber;;
}}
E implementar essa interface nas nossas contas PF e PJ, utilizandoE implementar essa interface nas nossas contas PF e PJ, utilizando
a palavra reservadaa palavra reservada implementsimplements mais o nome da nossa interface:mais o nome da nossa interface:
classclass ContaPJ extends Conta ContaPJ extends Conta implementsimplements Tributavel { Tributavel {
CalculaTributo():CalculaTributo(): numbernumber { {
//implementação do cálculo para o valor tributável para contas PJ//implementação do cálculo para o valor tributável para contas PJ
}}
}}
classclass ContaPF extends Conta ContaPF extends Conta implementsimplements Tributavel { Tributavel {
CalculaTributo():CalculaTributo(): numbernumber { {
//implementação do cálculo para o valor tributável para contas PF//implementação do cálculo para o valor tributável para contas PF
}}
}}
Note que, quando você implementou a interfaceNote que, quando você implementou a interface TributavelTributavel , o, o
compilador identificou que dentro desse contrato tem um métodocompilador identificou que dentro desse contrato tem um método
chamadochamado CalculaTributo()CalculaTributo() e sublinhou a classe em vermelhoe sublinhou a classe em vermelho
passando a notificação de que o passando a notificação de que o método deve ser implementado:método deve ser implementado:
//implementação//implementação
classclass ContaPJ ContaPJ implementsimplements Tributavel {} Tributavel {}
/* Resultado com o erro:/* Resultado com o erro:
class ContaPJclass ContaPJ
Class 'ContaPJ' incorrectly Class 'ContaPJ' incorrectly implements interface 'Tributavel'.implements interface 'Tributavel'.
Property 'CalculaTributProperty 'CalculaTributo' is o' is missing in type 'ContaPJ' but missing in type 'ContaPJ' but required inrequired in
type type 'ITributavel''ITributavel'.ts(2420).ts(2420)
conta.ts(2, 5): 'CalculaTributo' is declared conta.ts(2, 5): 'CalculaTributo' is declared here.here.
*/*/
Agora as nossas dua Agora as nossas duas contas devem implemens contas devem implementar o métodotar o método
CalculaTributo()CalculaTributo()
, que foi definido na , que foi definido na interfaceinterface
TributavelTributavel
, e adicionar , e adicionar o cálculo de acordo com o seu tipo de co cálculo de acordo com o seu tipo de conta, sendo PF ou PJ.onta, sendo PF ou PJ.
Com isso, finalizamos este cCom isso, finalizamos este capítulo sobre interfaces. No próximo,apítulo sobre interfaces. No próximo,
nós abordaremos os tipos genéricos.nós abordaremos os tipos genéricos.
C 6C 6
GenericsGenerics
Seguindo a definição deSeguindo a definição de GenericsGenerics na documentação do TypeScript, na documentação do TypeScript,
ele nos permite criar um ele nos permite criar um componente que pode funcionar em várioscomponente que pode funcionar em vários
tipos, em vez de em um tipos, em vez de em um único. Ao contrário dos tipos de objetos nãoúnico. Ao contrário dos tipos de objetos não
genéricos, os tipos genéricos só podem ser criados com interfacesgenéricos, os tipos genéricos só podem ser criados com interfaces
ou classes.ou classes.
O Generics não é O Generics não é exclusivo do TypeScripexclusivo do TypeScript. Quem já t. Quem já trabalha hátrabalha há
algum tempo com desenvolvimento de software, já algum tempo com desenvolvimento de software, já deve ter criadodeve ter criado
um método genérico ou já um método genérico ou já utilizou algum da linguagem com a qualutilizou algum da linguagem com a qual
trabalhou.trabalhou.
Um Generics muito utilizado é oUm Generics muito utilizado é o ArrayArray . Abrindo . Abrindo o GitHo GitHub do fonub do fontete
do Typedo TypeScript, nós podemos ver Script, nós podemos ver como ele foi definido utilizando umcomo ele foi definido utilizando um
Generics de uma interface que deve Generics de uma interface que deve retornar umretornar um TT ::
interfaceinterface Array<T> { Array<T> {
/**/**
* Determines whether an * Determines whether an array includes a certain element, returningarray includes a certain element, returning
true or false as appropriate.true or false as appropriate.
* @param searchElement The element to search for.* @param searchElement The element to search for.
* @param fromIndex The position in this array at * @param fromIndex The position in this array at which to beginwhich to begin
searching for searching for searchElement.searchElement.
*/*/
includes(searcincludes(searchElement: T, hElement: T, fromIndex?:fromIndex?: numbernumber):): booleanboolean;;
}}
Link do treLink do trecho de códicho de código no GitHgo no GitHub:ub:
https://github.cohttps://github.com/microsoft/Tm/microsoft/TypeScript/blob/maypeScript/blob/master/lib/lib.es201ster/lib/lib.es2016.arr 6.arr
ay.include.d.tsay.include.d.ts
Agora qual tip Agora qual tipo oo o TT representa? A princípio ainda não sabemos, sórepresenta? A princípio ainda não sabemos, só
vamos descobrir quando a interface for vamos descobrir quando a interface for implementada.implementada.
//Nesse exemplo T é uma string//Nesse exemplo T é uma string
constconst nomes: nomes: ArrayArray<<stringstring> = [> = ['pessoa1''pessoa1',,'pessoa2''pessoa2',,'pessoa3''pessoa3']]
//Nesse exemplo T é um number//Nesse exemplo T é um number
constconst dias: dias: ArrayArray<<numbernumber> = [5, 25, 28]> = [5, 25, 28]
Depois dessa rápida introdução, vamos criar alguns exemplosDepois dessa rápida introdução, vamos criar alguns exemplos
práticos.práticos.
6.1 Criando uma função genérica6.1 Criando uma função genérica
Para criar uma função genérica, basta Para criar uma função genérica, basta adicionar as chavesadicionar as chaves <T><T> ..
function function funcaoGenericafuncaoGenerica<T><T>()() {}{}
Agora podemos p Agora podemos passar qualquer tipassar qualquer tipo para funçãoo para função funcaoGenericafuncaoGenerica queque
ela deve aceitar:ela deve aceitar:
function function funcaoGenericafuncaoGenerica<T><T>()() {}{}
funcaoGenerica<funcaoGenerica<numbernumber>()>()
funcaoGenerica<funcaoGenerica<stringstring>()>()
funcaoGenerica<funcaoGenerica<booleanboolean>()>()
Ficou muito simples, não é? Ficou muito simples, não é? VVamos melhorar o nosso exemploamos melhorar o nosso exemplo
deixando-o mais próximo do nosso dia a dia:deixando-o mais próximo do nosso dia a dia:
function function funcaoGenericafuncaoGenerica<T><T>(value: T)(value: T): T: T {{
returnreturn value; value;
}}
Nessa função, nós devemos passar um tipo paraNessa função, nós devemos passar um tipo para funcaoGenerica<T>funcaoGenerica<T>,,
um argumento genérico e um um argumento genérico e um retorno genérico.retorno genérico.
function function funcaoGenericafuncaoGenerica<T><T>(value: T)(value: T): T: T {{
returnreturn value; value;
}}
consoleconsole.log(funcaoGenerica<.log(funcaoGenerica<NumberNumber>(1))>(1))
//retorna 1//retorna 1
consoleconsole.log(funcaoGenerica<.log(funcaoGenerica<stringstring>(>('teste''teste'))))
//retorna teste//retorna teste
consoleconsole.log(funcaoGenerica<.log(funcaoGenerica<booleanboolean>(true))>(true))
//retorna true//retorna true
Nós podemos também passar mais de um parâmetro para nossaNós podemos também passar mais de um parâmetro para nossa
função genérica:função genérica:
function fun<T, U, V>function fun<T, U, V>(args1:T, args2: U, args3: V)(args1:T, args2: U, args3: V): V: V {{
returnreturn args3; args3;
}}
consoleconsole.log(fun<.log(fun<stringstring,, numbernumber,, booleanboolean>(>('teste''teste', 1, true)), 1, true))
//retorna true//retorna true
Note que podemos passar outros valores Note que podemos passar outros valores no lugar deno lugar de TT ..
6.2 Criando uma classe genérica6.2 Criando uma classe genérica
Como nas funções genéricas, nós também podemos passar umComo nas funções genéricas, nós também podemos passar um
parâmetro genérico para uma classe.parâmetro genérico para uma classe.
classclass classeGenericaclasseGenerica<T> <T> {{
privateprivate arr: arr: ArrayArray<T> = [];<T> = [];
adicionaValor(adicionaValor(item: T) item: T) {{
thisthis.arr.push(item);.arr.push(item);
}}
retornaValoresretornaValores() () {{
returnreturn thisthis.arr;.arr;
}}
}}
letlet classeGenerica1 = classeGenerica1 = newnew classeGenericclasseGenerica<a<numbernumber>();>();
classeGenerica1.adicionaValor(4);classeGenerica1.adicionaValor(4);
consoleconsole.log(classeGe.log(classeGenerica1.retornanerica1.retornaValores());Valores());
//Retorna [ 4 ]//Retorna [ 4 ]
letlet classeGenerica2 = classeGenerica2 = newnew classeGenericclasseGenerica<a<stringstring>();>();
classeGenerica2.adicionaValor(classeGenerica2.adicionaValor('Homem de ferro''Homem de ferro'););
consoleconsole.log(classeGe.log(classeGenerica2.retornanerica2.retornaValores());Valores());
//Retorna [ 'Homem de ferro' ]//Retorna [ 'Homem de ferro' ]
6.3 Criando uma interface genérica6.3 Criando uma interface genérica
Para demonstrar a criação de Para demonstrar a criação de uma interface genérica, criaremosuma interface genérica, criaremos
uma interface chamadauma interface chamada InterfaceGenericaInterfaceGenerica com um método chamadocom um método chamado
removeItemremoveItem . Esse método deve ser utilizado para remover um item. Esse método deve ser utilizado para remover um item
da nossada nossa classeGenericaclasseGenerica ::
interfaceinterface InterfaceGenerica<I> { InterfaceGenerica<I> {
removeItem(iteremoveItem(item: m: I)I)
}}
classclass classeGenericaclasseGenerica<T><T> implementsimplements InterfaceGenerica<T> { InterfaceGenerica<T> {
//os outros métodos//os outros métodos
removeItem(itremoveItem(item: T) em: T) {{
letlet index = index = thisthis.arr.indexOf(item);.arr.indexOf(item);
ifif (index > -1) (index > -1)
thisthis.arr.splice(in.arr.splice(index, dex, 1);1);
}}
Validando a implementação do métodoValidando a implementação do método removeItemremoveItem na classena classe
classeGenericaclasseGenerica ::
letlet classeGenerica1 = classeGenerica1 = newnew classeGenericclasseGenerica<a<numbernumber>();>();
classeGenerica1.adicionaValor(1);classeGenerica1.adicionaValor(1);
classeGenerica1.adicionaValor(2);classeGenerica1.adicionaValor(2);
classeGenerica1.adicionaValor(3);classeGenerica1.adicionaValor(3);
consoleconsole.log(classeGe.log(classeGenerica1.retornanerica1.retornaValores());Valores());
//Retorna [ 1, 2, 3 ]//Retorna [ 1, 2, 3 ]
classeGenerica1.removeItem(1);classeGenerica1.removeItem(1);
consoleconsole.log(classeGe.log(classeGenerica1.retornanerica1.retornaValores());Valores());
//Retorna [ 2, 3 ]//Retorna [ 2, 3 ]
letlet classeGenerica2 = classeGenerica2 = newnew classeGenericclasseGenerica<a<stringstring>();>();
classeGenerica2.adicionaValor(classeGenerica2.adicionaValor('Homem de ferro''Homem de ferro'););
classeGenerica2.adicionaValor(classeGenerica2.adicionaValor('Homem aranha''Homem aranha'););
consoleconsole.log(classeGe.log(classeGenerica2.retornanerica2.retornaValores());Valores());
//Retorna [ 'Homem de ferro', 'Homem aranha' ]//Retorna [ 'Homem de ferro', 'Homem aranha' ]
classeGenerica2.removeItem(classeGenerica2.removeItem('Homem aranha''Homem aranha'););
consoleconsole.log(classeGe.log(classeGenerica2.retornanerica2.retornaValores());Valores());
//Retorna ['Homem de ferro']//Retorna ['Homem de ferro']
Com isso, finalizamos mais um módulo. Esse é um Com isso, finalizamos mais um módulo. Esse é um tema que algunstema que alguns
desenvolvedores iniciantes não conseguem aplicar no dia a dia, desenvolvedores iniciantes não conseguem aplicar no dia a dia, masmas
acredito que depois dos exemplos demonstrados você acredito que depois dos exemplos demonstrados você já estejajá esteja
pensando em como utilizá-lo.pensando em como utilizá-lo.
No próximo capítulo, nós veremos os decorators.No próximo capítulo, nós veremos os decorators.
C 7C 7
Decorator Decorator
Neste capítulo, nós abordaremos um recurso mNeste capítulo, nós abordaremos um recurso muito poderoso queuito poderoso que
podemos utilizar com podemos utilizar com TTypeScript, oypeScript, o decorator decorator ..
Os decorators nos permitemOs decorators nos permitem decorar decorar dinamicamente as dinamicamente as
características de uma classe.características de uma classe.
Atualmente, ele Atualmente, eles estão disponíveis como s estão disponíveis como um recurso experimentum recurso experimentalal
no Typeno TypeScript, mas mesmo Script, mas mesmo assim já estão presentes em assim já estão presentes em grandesgrandes
projetos de código aberto, como oprojetos de código aberto, como o Angular Angular e e InversifyInversify..
Para habilitar esse recurso, será necessário descomentar a Para habilitar esse recurso, será necessário descomentar a linha alinha a
seguir dentro do seu arquivoseguir dentro do seu arquivo tsconfig.jsontsconfig.json ..
"compilerOptions""compilerOptions": {: {
/*outras configurações*//*outras configurações*/
"experimentalDecorators""experimentalDecorators": true,: true, /* Enables experimental support for ES7/* Enables experimental support for ES7
Decorator. */Decorator. */
Caso você não se recorde do arquivoCaso você não se recorde do arquivo tsconfig.jsontsconfig.json , nós falamos, nós falamos
sobre ele no capítulo de sobre ele no capítulo de introdução deste livro.introdução deste livro.
Com essa funcionalidade habilitada no seu projeto, vamos a Com essa funcionalidade habilitada no seu projeto, vamos a algunsalguns
exemplos práticos para conhecê-lo melhor e ver como ele podeexemplos práticos para conhecê-lo melhor e ver como ele pode
ajudar no nosso dia a dia.ajudar no nosso dia a dia.
7.1 Analisando os decorators existentes no7.1 Analisando os decorators existentes no
TypeScriptTypeScript
Como mencionado, alguns projetos de código aberto já estãoComo mencionado, alguns projetos de código aberto já estão
utilizando os decorators. Quem já teve contato com outilizando os decorators. Quem já teve contato com o
desenvolvimento de uma aplicação Angular já se deparou comdesenvolvimento de uma aplicação Angular já se deparou com
algum dos decorators algum dos decorators adiante:adiante:
@NgModule @NgModule
@Component@Component
@Injectable@Injectable
@Directive@Directive
@Input@Input
@Output@Output
@ViewChild@ViewChild
Como você pode observar, um decorator é definido pelo caractereComo você pode observar, um decorator é definido pelo caractere
@@ mais o seu nome.mais o seu nome.
Agora, para qu Agora, para que possamos entendee possamos entender melhor comoé a criar melhor como é a criação de umção de um
decorator no Typdecorator no TypeScript, vamos navegar no seu eScript, vamos navegar no seu código-fonte e ver código-fonte e ver como os seus como os seus decorators foram definidos:decorators foram definidos:
declaredeclare type ClassDecorator = type ClassDecorator = <<TFunctionTFunction extendsextends FunctionFunction>>(target:(target:
TFunction) => TFunction | void;TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string |declare type PropertyDecorator = (target: Object, propertyKey: string |
symbol) => void;symbol) => void;
declare type MethodDecorator =declare type MethodDecorator = <<TT>>(target: Object, propertyKey: string |(target: Object, propertyKey: string |
symbol, symbol, descriptor: TypedPropertyDescriptodescriptor: TypedPropertyDescriptorr<<TT>>) =>) =>
TypedPropertyDescriptorTypedPropertyDescriptor<<TT>> | void; | void;
declare type ParameterDecorator = (target: Object, propertyKey: string |declare type ParameterDecorator = (target: Object, propertyKey: string |
symbol, parameterIndex: number) => void;symbol, parameterIndex: number) => void;
Trecho de código retirado do código-fonte do TypeScript queTrecho de código retirado do código-fonte do TypeScript que
atualmente está atualmente está no seguinte linno seguinte link do GitHk do GitH ub:ub:
https://github.cohttps://github.com/microsoft/Tm/microsoft/TypeScript/blob/dypeScript/blob/d28e38f57306a28e38f57306af063e1ef063e1e
e84432f8efa6e7c22093/src/lib/es5.d.tse84432f8efa6e7c22093/src/lib/es5.d.ts
Analisando os d Analisando os decoratorsecorators PropertyDecoratorPropertyDecorator ,, MethodDecoratorMethodDecorator ee
ParameterDecoratorParameterDecorator , podemos defini-los como funções que , podemos defini-los como funções que recebemrecebem
três parâmetros:três parâmetros:
target target (alvo).(alvo). propertyKey propertyKey (chave).(chave).
descriptor descriptor (descritor).(descritor).
Vamos analisar cada um deles:Vamos analisar cada um deles:
target (alvo):target (alvo): pode ser um método estático ou uma pode ser um método estático ou uma functionfunction
construtora de uma classe.construtora de uma classe.propertyKey (chave):propertyKey (chave): nome do membro da instância que será nome do membro da instância que será
utilizado no alvo.utilizado no alvo.
descdescriptoriptor r ((descdescritorritor)) :: a propriedade a propriedade descriptordescriptor do membrodo membro
da instância, chamando o métododa instância, chamando o método
Object.getOwnObject.getOwnPropertyDescripPropertyDescriptor()tor() ..
Para quem não conhece oPara quem não conhece o Object.getOwnPropertyDescriptor()Object.getOwnPropertyDescriptor() , ele, ele
retorna um descritor de propriedades para uma outra retorna um descritor de propriedades para uma outra propriedade.propriedade.
7.2 Criando um método decorator 7.2 Criando um método decorator
Depois dessa rápida introdução sobre o que é e como funciona umDepois dessa rápida introdução sobre o que é e como funciona um
decorator, nada melhor do que criarmos um para entender o seudecorator, nada melhor do que criarmos um para entender o seu
funcionamento.funcionamento.
Como vimos, basicamente precisamos criar uma Como vimos, basicamente precisamos criar uma função que recebefunção que recebe
três parâmetros:três parâmetros: targettarget ,, propertyKeypropertyKey ee descriptordescriptor . Para esse. Para esse
exemplo, vamos criar um exemplo, vamos criar um decorator para analisar o métododecorator para analisar o método
consultaSaldo()consultaSaldo() , da nossa classe, da nossa classe ContaConta , que vimos no capítulo sobre, que vimos no capítulo sobre
POO.POO.
function analisaSaldofunction analisaSaldo(target: any, key: any, (target: any, key: any, descriptor: any)descriptor: any) {}{}
Agora vamos decorar Agora vamos decorar o métodoo método consultaSaldo()consultaSaldo() da nossa classeda nossa classe
ContaConta ..
function analisaSaldofunction analisaSaldo(target: any, key: any, (target: any, key: any, descriptor: any)descriptor: any) {{
//implementação//implementação
}}
classclass Conta { Conta {
numeroDaConta:numeroDaConta: numbernumber;;
titular:titular: stringstring;;
saldo:saldo: numbernumber;;
constructorconstructor(numeroDaConta: number, titular: string, saldo: number) {(numeroDaConta: number, titular: string, saldo: number) {
thisthis.numeroDaCont.numeroDaConta a = = numeroDaContanumeroDaConta;;
thisthis.titular = titular;.titular = titular;
thisthis.saldo = saldo;.saldo = saldo;
}}
@analisaSaldo@analisaSaldo
consultaSaldo():consultaSaldo(): stringstring { {
returnreturn `O seu saldo atual é: ${ `O seu saldo atual é: ${thisthis.saldo}`;.saldo}`;
}}
}}
Resultado:Resultado:
Conta { consultaSaldo: [Conta { consultaSaldo: [FunctionFunction] } ] } consultaSaldo undefineconsultaSaldo undefinedd
Bem simples, não é? Bem simples, não é? VVejamos agora a criação de outros tipos deejamos agora a criação de outros tipos de
decorator que podemos utilizar cdecorator que podemos utilizar com TypeScripom TypeScript.t.
7.3 Decorator de propriedade7.3 Decorator de propriedade
Um decorator de propriedade pode ser definido por uma Um decorator de propriedade pode ser definido por uma função comfunção com
dois parâmetros:dois parâmetros:
target: target:
propertyKey:propertyKey:
OO targettarget é o protótipo da classe em que está sendo aplicado oé o protótipo da classe em que está sendo aplicado o
decorator, edecorator, e keykey é o nome da propriedade da classe.é o nome da propriedade da classe.
function validaTitularfunction validaTitular(target: any, propertyKey: any)(target: any, propertyKey: any) {{
//implementação//implementação
}}
classclass Conta { Conta {
numeroDaConta:numeroDaConta: numbernumber;;
@validaTitular@validaTitular
titular:titular: stringstring;;
saldo:saldo: numbernumber;;
constructorconstructor(numeroDaConta: number, titular: string, saldo: number) {(numeroDaConta: number, titular: string, saldo: number) {
thisthis.numeroDaCont.numeroDaConta a = = numeroDaContanumeroDaConta;;
thisthis.titular = titular;.titular = titular;
thisthis.saldo = saldo;.saldo = saldo;
}}
/* /* outros outros métodos*/métodos*/
}}
Resultado:Resultado:
Conta {} titularConta {} titular
7.4 Decorator de parâmetro7.4 Decorator de parâmetro
Um decorator deUm decorator de parameterparameter deve ser declarado antes da declaraçãodeve ser declarado antes da declaração
de um parâmetro e recebe três parâmetros. O primeiro, como nade um parâmetro e recebe três parâmetros. O primeiro, como na
maioria dos decorators que já vimos, é omaioria dos decorators que já vimos, é o targettarget , que é o protótipo, que é o protótipo
da classe. O segundo é oda classe. O segundo é o propertyKeypropertyKey , que é o nome do método que, que é o nome do método que
contém o parâmetro com o qual estamos trabalhando. O último é ocontém o parâmetro com o qual estamos trabalhando. O último é o
parameterIndexparameterIndex , que é o número da posição do parâmetro na função,, que é o número da posição do parâmetro na função,
lembrando que começa a partir dolembrando que começa a partir do 00 ..
function saldofunction saldo()() {{
returnreturn ( (
target:target: anyany,,
propertyKey:propertyKey: numbernumber,,
parameterIndex: parameterIndex: numbernumber,,
) => {) => {
consoleconsole.log(.log('target''target', target);, target);
consoleconsole.log(.log('property key''property key', , propertyKey);propertyKey);
consoleconsole.log(.log('parameter index''parameter index', , parameterIndex)parameterIndex);;
}}}}
classclass Conta { Conta {
numeroDaConta:numeroDaConta: numbernumber;;
titular:titular: stringstring;;
saldo:saldo: numbernumber;;
constructorconstructor(numeroDaConta: number, titular: string, saldo: number) {(numeroDaConta: number, titular: string, saldo: number) {
thisthis.numeroDaCont.numeroDaContaa = = numeroDaContanumeroDaConta;;
thisthis.titular = titular;.titular = titular;
thisthis.saldo = saldo;.saldo = saldo;
}}
adicionaSaldo(adicionaSaldo(@saldo() @saldo() saldo:saldo: numbernumber):): voidvoid { {
thisthis.saldo + saldo;.saldo + saldo;
}}
}}
7.5 Criando um decorator para class7.5 Criando um decorator para class
O decorator para classe deve ser declarado antes da declaração daO decorator para classe deve ser declarado antes da declaração da
própria classe. Esse decorator recebe um único parâmetro, que é oprópria classe. Esse decorator recebe um único parâmetro, que é o
construtor da classe.construtor da classe.
function logfunction log(ctor: any)(ctor: any) {{
consoleconsole.log(ctor).log(ctor)
}}
@log@log
classclass Conta {} Conta {}
Resultado:Resultado:
[[FunctionFunction: Conta]: Conta]
7.6 D7.6 Decoraecorator Factotor Factoryry
Em alguns cenários em que precisamos fazer uma interação entreEm alguns cenários em que precisamos fazer uma interação entre
uma classeuma classe targettarget e um decorator, como na implementação de ume um decorator, como na implementação de um
log onde precisamos passar um log onde precisamos passar um determinado valor para o decorator determinado valor para o decorator
para que ele tome uma ação, como escrever esse valor em para que ele tome uma ação, como escrever esse valor em umum
arquivoarquivo .txt.txt ou enviar um eventoou enviar um evento mensagemmensagem para uma API, nós para uma API, nós
podemos utilizar o decoratorpodemos utilizar o decorator FactoryFactory ..
Sua sintaxe é a mesma que a de Sua sintaxe é a mesma que a de um decorator de uma classe, comum decorator de uma classe, com
a pequena diferença de que ele recebe um valor através de uma pequena diferença de que ele recebe um valor através de um
parâmetro. A seguir você tem um exemplo, onde estamos passandoparâmetro. A seguir você tem um exemplo, onde estamos passando
para o decoratorpara o decorator analisaContaanalisaConta a informação de que a classe que oa informação de que a classe que o
está implementando é do tipoestá implementando é do tipo PJPJ ..
function analisaContafunction analisaConta(tipoConta: any)(tipoConta: any) {{
returnreturn (_target: (_target: anyany) => {) => {
consoleconsole.log(`${tipoCon.log(`${tipoConta} ta} - - ${_target}`);${_target}`);
}}
}}
@analisaConta(@analisaConta('PJ''PJ'))
classclass Conta {} Conta {}
Resultado:Resultado:
PJ -PJ - function Contafunction Conta()() { }{ }
7.7 M7.7 Múltipúltiplos decorlos decoratorsators
Em alguns cenários, precisaremos criar mais de um decorator paraEm alguns cenários, precisaremos criar mais de um decorator para
as nossas classes. Para ficar mais claro, imagine o seguinteas nossas classes. Para ficar mais claro, imagine o seguinte
cenário: chegou uma demanda e agora nós cenário: chegou uma demanda e agora nós precisamos criar umprecisamos criar um
decorator dedecorator de loglog , um outro para validação de uma regra de negócio, um outro para validação de uma regra de negócio
X e um outro para verificar se a X e um outro para verificar se a conta éconta é PJPJ ouou PFPF ..
Como podemos Como podemos implementá-los?implementá-los?
O TypeScripO TypeScript nos permite implementar mais t nos permite implementar mais de um decorator emde um decorator em
uma classe. Seguindo o nosso uma classe. Seguindo o nosso exemplo, teríamos algo como:exemplo, teríamos algo como:
function analisaContafunction analisaConta(tipoConta: string)(tipoConta: string) {{
returnreturn (_target: (_target: anyany) => {) => {
consoleconsole.log(`${tipoCon.log(`${tipoConta} ta} - - ${_target}`);${_target}`);
}}
}}
function logfunction log(ctor: any)(ctor: any) { }{ }function validaRegrafunction validaRegra(ctor: any)(ctor: any) { }{ }
@log@log
@validaRegra@validaRegra
@analisaConta(@analisaConta('PJ''PJ'))
classclass Conta { } Conta { }
Com isso, nós finalizamos mais este capítulo. No próximo, veremosCom isso, nós finalizamos mais este capítulo. No próximo, veremos
como organizar o nosso ccomo organizar o nosso código utilizando namespaces.ódigo utilizando namespaces.
C 8C 8
MM odules odules e nae namespacesmespaces
Neste capítulo, nós abordaremos algumas formas de Neste capítulo, nós abordaremos algumas formas de organizar oorganizar o
nosso código utilizandonosso código utilizando modulesmodules e e namespacesnamespaces..
8.1 Namespaces8.1 Namespaces
Namespace não é algo exclusivo Namespace não é algo exclusivo do Typedo TypeScript. Quem programaScript. Quem programa
em C# já em C# já está habituado a sua utilização por ele ser padrão naestá habituado a sua utilização por ele ser padrão na
criação de uma classe em C#.criação de uma classe em C#.
Mas caso esse seja o Mas caso esse seja o seu primeiro contato com ele, saiba que oseu primeiro contato com ele, saiba que o
namespace nos permite organizar o nosso namespace nos permite organizar o nosso código, deixando-ocódigo, deixando-o
agrupado por nome. Para que esse agrupado por nome. Para que esse conceito fique mais claro, nadaconceito fique mais claro, nada
melhor que um exemplo prático.melhor que um exemplo prático.
VVamos voltar ao amos voltar ao capítulo 4, sobre capítulo 4, sobre Programação Orientada aProgramação Orientada a
Objetos, no qual desenvolvemos uma classeObjetos, no qual desenvolvemos uma classe abstractabstract chamadachamada
ContaConta , e a partir dela nós criamos a, e a partir dela nós criamos a ContaPFContaPF ee ContaPJContaPJ ..
Tendo esse cenário em mente, imagine que agora chegou uma novaTendo esse cenário em mente, imagine que agora chegou uma novademanda para criarmos duas novas implementações da cdemanda para criarmos duas novas implementações da classelasse
ContaConta , uma para, uma para ContaSalarioContaSalario e uma outra parae uma outra para ContaInvestimentoContaInvestimento ..
Até aqui está b Até aqui está bem simples, basta criarmoem simples, basta criarmos algo como no exemps algo como no exemplo alo a
seguir para resolver a nossa seguir para resolver a nossa demanda.demanda.
classclass ContaSalario extends Conta {} ContaSalario extends Conta {}
classclass ContaInvestimento extends Conta {} ContaInvestimento extends Conta {}
Mas com essa resolução, nós chegamos a um Mas com essa resolução, nós chegamos a um grande problema quegrande problema que
surge com o crescimento dos nossos sistemas: a surge com o crescimento dos nossos sistemas: a organização deorganização de
todas as nossas classes.todas as nossas classes.
Para evitar que o sPara evitar que o sistema fique caótico, podemos agrupar asistema fique caótico, podemos agrupar as
classes por características comuns e dar um nome para cada umclasses por características comuns e dar um nome para cada um
desses grupos. Isto é, agrupar um conjunto de classes em umdesses grupos. Isto é, agrupar um conjunto de classes em um
espaço em comum e lhe dar um espaço em comum e lhe dar um nome, por exemplonome, por exemplo BancoBanco . Esse. Esse
espaço definido por um nome é espaço definido por um nome é chamado de namespace.chamado de namespace.
Atualizando o Atualizando o nosso código com a utinosso código com a utilização do namespalização do namespace nósce nós
teríamos algo como no exemplo a seguir:teríamos algo como no exemplo a seguir:
namespace Banco {namespace Banco {
exportexport classclass Conta { Conta {
numeroDaConta:numeroDaConta: numbernumber;;
titular:titular: stringstring;;
saldo:saldo: numbernumber;;
/* outras implementações *//* outras implementações */
}}
}}
Agora para he Agora para herdarmos a nossa classerdarmos a nossa classe ContaConta de um local fora dode um local fora do
namespacenamespace BancoBanco , nós precisamos passar o nome do namespace, nós precisamos passar o nome do namespace
antes do nome da classe:antes do nome da classe:
classclass ContaPF extends Banco.Conta {} ContaPF extends Banco.Conta {}
classclass ContaPJ extends Banco.Conta{} ContaPJ extends Banco.Conta {}
Caso contrário, o compilador retornará o seguinte erro:Caso contrário, o compilador retornará o seguinte erro:
classclass ContaPF extends Conta {} ContaPF extends Conta {}
classclass ContaPJ extends Conta {} ContaPJ extends Conta {}
Resultado:Resultado:
//Erro Cannot find name //Erro Cannot find name 'Conta'.ts(23'Conta'.ts(2304)04)
Analisando o n Analisando o nosso código, não fosso código, não ficaria mais organizaicaria mais organizado se todas asdo se todas as
nossas implementações que surgiram a partir da nossas implementações que surgiram a partir da classeclasse ContaConta
fossem agrupadas pelo mesmo namespacefossem agrupadas pelo mesmo namespace BancoBanco ? Sim, pois todas? Sim, pois todas
estão no mesmo contexto.estão no mesmo contexto.
A seguir você tem um e A seguir você tem um exemplo de como ficarixemplo de como ficaria essa implementaçãoa essa implementação::
namespace Banco {namespace Banco {
exportexport classclass ContaPF extends Conta { } ContaPF extends Conta { }
}}
namespace Banco {namespace Banco {
exportexport classclass ContaPJ extends Conta { } ContaPJ extends Conta { }
}}
namespace Banco {namespace Banco {
exportexport classclass ContaSalario extends Conta { } ContaSalario extends Conta { }
}}
namespace Banco {namespace Banco {
exportexport classclass ContaInvestimento extends Conta { } ContaInvestimento extends Conta { }
}}
Namespaces aninhadosNamespaces aninhados
Um outro recurso que nós temos ao trabalhar com namespaces é aUm outro recurso que nós temos ao trabalhar com namespaces é a
definição de um namespace dentro de outro. Meio definição de um namespace dentro de outro. Meio confuso, não?confuso, não?
Para ficar mais claro, vamos voltar ao exemplo do namespacePara ficar mais claro, vamos voltar ao exemplo do namespace
BancoBanco , dentro do qual nós agrupamos as classes, dentro do qual nós agrupamos as classes ContaPJContaPJ ,, ContaPFContaPF ,,
ContaSalarioContaSalario ee ContaInvestimentoContaInvestimento . Sim, elas pertencem ao. Sim, elas pertencem ao
namespacenamespace BancoBanco , mas não seria interessante se elas estivessem, mas não seria interessante se elas estivessem
agrupadas por investimento também?agrupadas por investimento também?
Tendo esse cenário em mente, nós podemos atualizar o nossoTendo esse cenário em mente, nós podemos atualizar o nosso
código da seguinte forma:código da seguinte forma:
namespace Banco {namespace Banco {
exportexport namespace Investimento { namespace Investimento {
exportexport classclass ContaSalario extends Conta { } ContaSalario extends Conta { }
}}
}}
namespace Banco {namespace Banco {
exportexport namespace Investimento { namespace Investimento {
exportexport classclass ContaInvestimento extends Conta { } ContaInvestimento extends Conta { }
}}
}}
Agora, para qu Agora, para que possamos instanciar ee possamos instanciar essas classes, basta passar ssas classes, basta passar
os namespaces antes do nome da classe, como no exemplo aos namespaces antes do nome da classe, como no exemplo a
seguir:seguir:
letlet novaContaInvestimento = novaContaInvestimento = newnew Banco.InvestimeBanco.Investimento.ContaInvesnto.ContaInvestimento();timento();
letlet novaContaSalario = novaContaSalario = newnew Banco.InvestimeBanco.Investimento.ContaSalarnto.ContaSalario();io();
Mas se a ideia é organizar o nosso código, como devemos fazer Mas se a ideia é organizar o nosso código, como devemos fazer
para separá-lo em arquivos diferentes? Complicou?para separá-lo em arquivos diferentes? Complicou?
Na realidade é bem simples, basta chamá-los da mesma forma queNa realidade é bem simples, basta chamá-los da mesma forma que
á fizemos, sem á fizemos, sem a necessidade de nenhuma referência.a necessidade de nenhuma referência.
//contaInvestimento.ts//contaInvestimento.ts
namespace Banco {namespace Banco {
exportexport namespace Investimento { namespace Investimento {
exportexport classclass ContaInvestimento extends Conta { } ContaInvestimento extends Conta { }
}}
}}
//contaSalario.ts//contaSalario.ts
namespace Banco {namespace Banco {
exportexport namespace Investimento { namespace Investimento {
exportexport classclass ContaSalario extends Conta { } ContaSalario extends Conta { }
}}
}}
Agora chamando-o Agora chamando-os de um outro arqs de um outro arquivo:uivo:
//index.ts//index.ts
letlet novaContaInvestimento = novaContaInvestimento = newnew Banco.InvestimeBanco.Investimento.ContaInvesnto.ContaInvestimento();timento();
letlet novaContaSalario = novaContaSalario = newnew Banco.InvestimeBanco.Investimento.ContaSalarnto.ContaSalario();io();
8.2 8.2 MM oduloduleses
Como os namespaces, osComo os namespaces, os modulesmodules no TypeScript nos ajudam a no TypeScript nos ajudam a
organizar o nosso código separando as nossas classes. Elesorganizar o nosso código separando as nossas classes. Eles
utilizam o mesmo conceito dos namespaces com a utilização dautilizam o mesmo conceito dos namespaces com a utilização da
palavra reservadapalavra reservada exportexport , mas com algumas diferenças: para que, mas com algumas diferenças: para que
possamos trabalhar com eles, nós precisamos de umpossamos trabalhar com eles, nós precisamos de um module loadermodule loader
e, para chamar ume, para chamar um modulemodule de um outro arquivo, nós precisamosde um outro arquivo, nós precisamos
utilizar a palavra reservadautilizar a palavra reservada importimport ..
Para quem não conhece, umPara quem não conhece, um module loadermodule loader é uma biblioteca que nosé uma biblioteca que nos
permite trabalhar com os modules nos nossos projetos. Aspermite trabalhar com os modules nos nossos projetos. As
bibliotecas mais conhecidas são:bibliotecas mais conhecidas são: CommonJsCommonJs ee Require.jsRequire.js ..
Na parte prática deste livro, nós Na parte prática deste livro, nós utilizaremos os modules parautilizaremos os modules para
organização do nosso código, mas, para que você já possa ter umaorganização do nosso código, mas, para que você já possa ter uma
base de como utilizá-lo, vamos base de como utilizá-lo, vamos criar alguns exemplos agora.criar alguns exemplos agora.
Alterando o n Alterando o nosso exemplo anterioosso exemplo anterior com os namespaces emr com os namespaces em
arquivos separados, utilizando os modules nós teríamos o arquivos separados, utilizando os modules nós teríamos o seguinteseguinte
resultado:resultado:
//conta.ts//conta.ts
exportexport classclass Conta { Conta {
numeroDaConta:numeroDaConta: numbernumber;;
titular:titular: stringstring;;
saldo:saldo: numbernumber;;
/* outras implementações *//* outras implementações */
}}
//contaInvestimento.ts//contaInvestimento.ts
importimport { Conta } from { Conta } from "./Conta""./Conta";;
exportexport classclass ContaInvestimento extends Conta { } ContaInvestimento extends Conta { }
//contaSalario.ts//contaSalario.ts
importimport { Conta } from { Conta } from "./Conta""./Conta";;
exportexport classclass ContaSalario extends Conta { } ContaSalario extends Conta { }
//index.ts//index.ts
importimport { ContaInvestimento } from { ContaInvestimento } from "./contaInvestimento""./contaInvestimento";;
importimport { ContaSalario } from { ContaSalario } from "./contaSalario""./contaSalario";;
letlet novaContaInvestimento = novaContaInvestimento = newnew ContaInvestimenContaInvestimento();to();
letlet novaContaSalario = novaContaSalario = newnew ContaSalario();ContaSalario();
Note que eu removi todos os namespaces do exemplo anterior e,Note que eu removi todos os namespaces do exemplo anterior e,
para importar os nossos modulespara importar os nossos modules ContaConta ,, ContaSalarioContaSalario ee
ContaInvestimentoContaInvestimento , estou utilizando a palavra reservada, estou utilizando a palavra reservada importimport ..
8.3 8.3 MM odules odules ou nou namespaceamespaces? s? QQuando uando utiliutilizar?zar?Agora que nós co Agora que nós conhecemos os modules e nhecemos os modules e os namespaces vemos namespaces vem
aquela dúvida, quando utiliaquela dúvida, quando utilizar um ou o outro? zar um ou o outro? Qual é a principalQual é a principal
diferença entre eles?diferença entre eles?
Para responder a essas e outras perguntas sobre esse assunto, aPara responder a essas e outras perguntas sobre esse assunto, a
seguir você tem alguns pontos sobre cada um:seguir você tem alguns pontos sobre cada um:
Começando com os namespaces:Começando com os namespaces:
TrabTrabalham com escopo global, por alham com escopo global, por esse motivo não precisamosesse motivo não precisamos
importá-los como nos modules.importá-los como nos modules.
A organização do A organização dos namespaces é mais lógis namespaces é mais lógica e feita emca e feita em
objetos, independentobjetos, independente de estar e de estar em um ou em um ou mais arquivos.mais arquivos.
Um namespace pode ser utilizado em mais de um arquivo,Um namespace pode ser utilizado em mais de um arquivo,
como vimos nos exemplos.como vimos nos exemplos.
Eles não precisam de Eles não precisam de nenhumanenhuma bibliotecabiblioteca para ser utilizado. para ser utilizado.
Agora falando Agora falando nos modules, temos:nos modules, temos:
Não ficam no escopo global, por esse motivo precisamos utilizar Não ficam no escopo global, por esse motivo precisamos utilizar
a palavra reservadaa palavra reservada importimport ..
Toda implementação se torna um module separado.Toda implementação se torna um module separado.
Eles precisam de umaEles precisam de uma bibliotecabiblioteca, como a, como a CommonJsCommonJs ou aou a
Require.js Require.js para serem utilizados.para serem utilizados.
Com isso, nós finalizamos mais um capítulo. No próximo, nósCom isso, nós finalizamos mais um capítulo. No próximo, nós
iniciaremos a parte prática deste livro. Mãos na massa!iniciaremos a parte prática deste livro. Mãos na massa!
C 9C 9
Visual Studio CodeVisual Studio Code
Como vimos na introdução do livro, para a parte prática vamosComo vimos na introdução do livro, para a parte prática vamos
utilizar o Visual Studio Code, ou VS utilizar o Visual Studio Code, ou VS Code pela sua integração com oCode pela sua integração com o
TypeScript.TypeScript.
Caso você ainda não o tenha instalado, basta acessar o link:Caso você ainda não o tenha instalado, basta acessar o link:
https://code.visualstudio.com/Downloadhttps://code.visualstudio.com/Download e fazer o download de uma e fazer o download de uma
versão de acordo com versão de acordo com o seu sistema o seu sistema operacional, Windows, Linuxoperacional, Windows, Linux
ou Mac.ou Mac.
Caso esse seja o seu primeiro contato com o VS Caso esse seja o seu primeiro contato com o VS Code, a seguir Code, a seguir
estou destacando alguns pontos que podem otimizar o estou destacando alguns pontos que podem otimizar o seu trabalhoseu trabalhocom ele. Caso você já o utilize no scom ele. Caso você já o utilize no seu dia a dia, recomendo que puleeu dia a dia, recomendo que pule
para o próximo capítulo.para o próximo capítulo.
Seguindo a documentação oficial do VS Code, nós Seguindo a documentação oficial do VS Code, nós podemos dividi-podemos dividi-
lo em cinco partes:lo em cinco partes:
Editor — Área onde editamos os arquivos. O VS Code nosEditor — Área onde editamos os arquivos. O VS Code nos
permite abrir mais de um arquivo lado a lado.permite abrir mais de um arquivo lado a lado.
Side Bar — Podemos Side Bar — Podemos adicionar alguns plugins ou, comoadicionar alguns plugins ou, como
default, deixar o plugin do Git default, deixar o plugin do Git que fica monitorando as nossasque fica monitorando as nossasalterações.alterações.
Status Bar — Informação sobre Status Bar — Informação sobre o projeto aberto na solução.o projeto aberto na solução.
Activity Bar — A meu ver Activity Bar — A meu ver, depois do Edit, depois do Editor Groups, essa é aor Groups, essa é a
parte que nós mais utilizamos. Nela temos um painel queparte que nós mais utilizamos. Nela temos um painel que
podemos abrir e fechar, um botão depodemos abrir e fechar, um botão de searchsearch, um controlador de, um controlador de
versão e o versão e o melhormelhor, um local para , um local para que possamos gerenciar osque possamos gerenciar os
nossos plugins.nossos plugins.
Panels — No painel, nós podemos monitorar os erros dePanels — No painel, nós podemos monitorar os erros de debug debug ,,
temos otemos o output output e o melhor, podemos trabalhar com um ou mais e o melhor, podemos trabalhar com um ou mais
terminais sem precisar utilizar o CMD do Windows ou terminalterminais sem precisar utilizar o CMD do Windows ou terminal
do Linux/MAC. Essa é do Linux/MAC. Essa é uma das funcionalidades que eu acreditouma das funcionalidades que eu acredito
que a maioria dos desenvolvedores front-end e back-end que a maioria dos desenvolvedores front-end e back-end maismais
utiliza.utiliza.
A seguir você tem uma A seguir você tem uma imagem destacando imagem destacando cada uma das partecada uma das partes:s:
Figura 9.1: Visual Studio Code.Figura 9.1: Visual Studio Code.
AtalhosAtalhos
Como toda IDE, o VS Code tem muitos atalhos. Para ver uma Como toda IDE, o VS Code tem muitos atalhos. Para ver uma listalista
com todos eles, vá atécom todos eles, vá até file -> preferences -> Keyboard Shortcutsfile -> preferences -> Keyboard Shortcuts ou ou
utilize um atalho (no Windows):utilize um atalho (no Windows): Ctrl + k Ctrl + SCtrl + k Ctrl + S . A seguir você tem. A seguir você tem
uma imagem mostrando a lista de atalhos que nós temos no VSuma imagem mostrando a lista de atalhos que nós temos no VS
Code.Code.
Figura 9.2: Visual Studio Code - Atalhos.Figura 9.2: Visual Studio Code - Atalhos.
Aproveito para d Aproveito para destacar os que eu estacar os que eu mais utilizo no meu mais utilizo no meu dia a dia,dia a dia,
pois pode facilitar para você:pois pode facilitar para você:
ctrl + ctrl + ff : busca no arquivo que está em foco.: busca no arquivo que está em foco.
ctrl + ctrl + pp : busca um arquivo dentro da: busca um arquivo dentro da solutionsolution ..
ctrl + ctrl + shifshift + t + pp : para executar algum comando da IDE, por : para executar algum comando da IDE, por
exemplo, desabilitar todos os exemplo, desabilitar todos os breakpoints.breakpoints.
ctrl + ctrl + shifshift + t + gg : mostra todas as alterações feitas no projeto no: mostra todas as alterações feitas no projeto no
seu repositório local.seu repositório local.
IntelliSenseIntelliSense
O IntelliSense é uma ajuda de O IntelliSense é uma ajuda de preenchimento de código que incluipreenchimento de código que inclui
inúmeras inúmeras funcionalidadefuncionalidades:s: Listar MembrosListar Membros,, Informações doInformações do
ParâmetroParâmetro,, Informações RápidasInformações Rápidas e e Completar PalavraCompletar Palavra..
Essas funcionalidades ajudam você a aprender mais sEssas funcionalidades ajudam você a aprender mais sobre o códigoobre o código
que está usando, a manter que está usando, a manter o acompanhamento dos parâmetros queo acompanhamento dos parâmetros que
está digitando e a adicionar chamadas a está digitando e a adicionar chamadas a métodos e propriedadesmétodos e propriedadespressionando apenas algumas teclas.pressionando apenas algumas teclas.
A seguir você tem uma A seguir você tem uma imagem demonstrandimagem demonstrando essa funcionalido essa funcionalidade:ade:
Figura 9.3: Visual Studio Code - IntelliSense.Figura 9.3: Visual Studio Code - IntelliSense.
SnippetsSnippets
Os snippets são blocos de códigos que podem ser utilizados paraOs snippets são blocos de códigos que podem ser utilizados para
ajudar a agilizar o trabalho de quem ajudar a agilizar o trabalho de quem desenvolve. A ideia é facilitar adesenvolve. A ideia é facilitar a
utilização de fragmentos de códigos repetitivos em diversas utilizaçãode fragmentos de códigos repetitivos em diversas partespartes
da aplicação que está sendo desenvolvida.da aplicação que está sendo desenvolvida.
No VS Code, os No VS Code, os snippets aparecem junto do IntelliSense. Utilizandosnippets aparecem junto do IntelliSense. Utilizando
oo Ctrl+SpaceCtrl+Space você tem um retorno com algumas sugestões devocê tem um retorno com algumas sugestões de
preenchimento de código, como chamada a um preenchimento de código, como chamada a um método de umamétodo de uma
classe ou a criação de umaclasse ou a criação de uma functionfunction completa. A seguir você temcompleta. A seguir você temum exemplo de um snippet no VS Code.um exemplo de um snippet no VS Code.
Figura 9.4: Visual Studio - Code Snippets.Figura 9.4: Visual Studio - Code Snippets.
JJSDSDoc suoc suppopportrt
O IntelliSense no VS Code O IntelliSense no VS Code exibe informações que você podeexibe informações que você pode
adicionar a um script usando adicionar a um script usando comentárioscomentários JSDoc JSDoc padrão. Para padrão. Paraquem não conhece, o JSDoc é uma linguagem de marcação quequem não conhece, o JSDoc é uma linguagem de marcação que
podemos utilizar para documentar código podemos utilizar para documentar código JavaScript.JavaScript.
Ele nos permite usar cEle nos permite usar comentários para fornecer informações sobreomentários para fornecer informações sobre
elementos de código, como funções, campos elementos de código, como funções, campos e variáveis.e variáveis.
A seguir você tem as ma A seguir você tem as marcas que podem ser urcas que podem ser utilizadas paratilizadas para
adicionar informações do seu código utilizando o JSDoc:adicionar informações do seu código utilizando o JSDoc:
@deprecated @deprecated : especifica uma função ou : especifica uma função ou um método preterido.um método preterido.
@description @description : especifica a descrição de uma função ou um: especifica a descrição de uma função ou um
método.método.
@param @param : especifica informações para um parâmetro em uma: especifica informações para um parâmetro em uma
função ou método. O TypeScript também dá suporte afunção ou método. O TypeScript também dá suporte a
@paramTag @paramTag ..
@property @property : especifica informações, incluindo uma descrição para: especifica informações, incluindo uma descrição para
um campo ou membro definido em um objeto.um campo ou membro definido em um objeto.
@returns @returns : especifica um valor de retorno.: especifica um valor de retorno.
@summary @summary : especifica a descrição de uma função ou método.: especifica a descrição de uma função ou método.
@type @type : especifica o tipo para uma constante ou uma variável.: especifica o tipo para uma constante ou uma variável.
@typedef @typedef : especifica um tipo : especifica um tipo personalizado.personalizado.
Para ficar mais claro, vamos ver um Para ficar mais claro, vamos ver um exemplo do uso das marcasexemplo do uso das marcas
@description@description ee @param@param utilizando um trecho de código retirado doutilizando um trecho de código retirado do
nosso capítulo 4 sobre Orientação a nosso capítulo 4 sobre Orientação a Objetos.Objetos.
/** @description método criado para adicionar saldo /** @description método criado para adicionar saldo para um cliente.para um cliente.
* @param {number} saldo parametro criado para adicionar um bonus no saldo* @param {number} saldo parametro criado para adicionar um bonus no saldo
de um cliente.de um cliente.
*/*/
protected protected adicionaSaldo(adicionaSaldo(saldo:saldo: numbernumber):): voidvoid { {
thisthis.saldo + saldo;.saldo + saldo;
}}
Resultado:Resultado:
Figura 9.5: Visual Studio - JSDoc.Figura 9.5: Visual Studio - JSDoc.
Auto importsAuto imports
Quando você está importando uma biblioteca de terceiros ou Quando você está importando uma biblioteca de terceiros ou umum
módulo do seu projeto, o VS módulo do seu projeto, o VS Code informará que ele não conheceCode informará que ele não conhece
essa classe ou método e vai percorrer pela sua solução,essa classe ou método e vai percorrer pela sua solução, seu projetoseu projeto,,
e procurar esse código retornando algumas sugestões de e procurar esse código retornando algumas sugestões de importimport
para que você possa para que você possa importar esse módulo, script ou importar esse módulo, script ou biblioteca.biblioteca.
Para ficar mais claro, vamos voltar ao capítulo anterior sobrePara ficar mais claro, vamos voltar ao capítulo anterior sobre
namespaces. Caso você tente chamar o namespacenamespaces. Caso você tente chamar o namespace BancoBanco de umde um
outro arquivo, o VS Code outro arquivo, o VS Code retornará o seguinte erro:retornará o seguinte erro:
Figura 9.6: Visual Studio - Erro de referência.Figura 9.6: Visual Studio - Erro de referência.
E caso você clique na lâmpada azul, que está no canto superior E caso você clique na lâmpada azul, que está no canto superior
esquerdo da imagem anterioresquerdo da imagem anterior, a IDE , a IDE vai sugerir alguns imports paravai sugerir alguns imports para
que você possa adicioná-los ao arquivo que que você possa adicioná-los ao arquivo que está tentandoestá tentando
referenciar. No nosso exemplo anterior, ele vai retornar asreferenciar. No nosso exemplo anterior, ele vai retornar as
referências para o namespacereferências para o namespace BancoBanco ..
Figura 9.7: Visual Studio - Auto Import.Figura 9.7: Visual Studio - Auto Import.
Code navigationCode navigation
OO Code navigationCode navigation, ou, em , ou, em português, navegação de código, nosportuguês, navegação de código, nos
permite navegar pelo código de uma maneira mais permite navegar pelo código de uma maneira mais rápida. A seguir rápida. A seguir
você tem uma lista de teclas que podem nos ajudar a navegar pelovocê tem uma lista de teclas que podem nos ajudar a navegar pelo
nosso código.nosso código.
F12 F12
: leva-nos para a : leva-nos para a definição do nosso código.definição do nosso código.
Alt + Alt + F12F12 : leva-nos para o ponto anterior à navegação com o: leva-nos para o ponto anterior à navegação com o
F12.F12.
Shift + Shift + F12F12 : mostra todos os lugares no nosso código que estão: mostra todos os lugares no nosso código que estão
referenciando um determinado trecho de referenciando um determinado trecho de código.código.
Ctrl + Ctrl + F12F12 : leva-nos para a : leva-nos para a implementação de uma interface.implementação de uma interface.
RenameRename
No VS Code, quando pressionamos oNo VS Code, quando pressionamos o F2F2 em cima de um em cima de um método,método,
nós conseguimos renomeá-lo, bem como todas as nós conseguimos renomeá-lo, bem como todas as suas referências.suas referências.
Figura 9.8: Visual Studio - Rename.Figura 9.8: Visual Studio - Rename.
RefactoringRefactoring
O VS Code tem algumas sugestões de refatoração rápida, comoO VS Code tem algumas sugestões de refatoração rápida, como
para extrair uma classe ou interface para um outro arquivo. Parapara extrair uma classe ou interface para um outro arquivo. Para
que você possa ver as sugestões de refatoração, basta pressionar que você possa ver as sugestões de refatoração, basta pressionar
Control + .Control + . ..
Figura 9.9: Visual Studio - Refactoring.Figura 9.9: Visual Studio - Refactoring.
References CodeLensReferences CodeLens
Quando habilitadoQuando habilitado CodeLensCodeLens no VS Code, ele mostra todas as no VS Code, ele mostra todas asreferências das suas classes, referências das suas classes, interfaces, métodos, propriedades, einterfaces, métodos, propriedades, e
os objetos exportados.os objetos exportados.
Figura 9.10: Visual Studio - CodeLens.Figura 9.10: Visual Studio - CodeLens.
Para habilitá-lo é bem simples, basta utilizar o Para habilitá-lo é bem simples, basta utilizar o atalhoatalho Control + ShiftControl + Shift
+ + PP , abrir o arquivo, abrir o arquivo Settings.jsSettings.js e adicionar a seguinte linhaa e adicionar a seguinte linha a ele:ele:
typescript.retypescript.referencesCodeLenferencesCodeLens.enabled": s.enabled": truetrue ..
DebuggingDebugging
O suporte aO suporte a debug debug é um dos é um dos motivos pelo qual eu sempremotivos pelo qual eu sempre
recomendo a utilização do VS Code recomendo a utilização do VS Code para o desenvolvimento depara o desenvolvimento de
projeto com TypeScript. Ele tem suporte projeto com TypeScript. Ele tem suporte para projetos desenvolvidospara projetos desenvolvidos
no back-end utilizando Node.js com TypeScript ou no front-end emno back-end utilizando Node.js com TypeScript ou no front-end em
projeto TypeScript com Angular, por exemplo.projeto TypeScript com Angular, por exemplo.
Para fazer debug de um projeto, basta clicar no canto esquerdo daPara fazer debug de um projeto, basta clicar no canto esquerdo da
IDE no ícone deIDE no ícone de PlayPlay com uma com uma baratinhabaratinha..
Figura 9.11: Visual Studio - debug.Figura 9.11: Visual Studio - debug.
TypeScript extensionsTypeScript extensions
Para finalizar este capítulo, o VS Para finalizar este capítulo, o VS Code tem muitos plugins queCode tem muitos plugins que
podem ajudar no nosso dia a dia. Para explorar essa lista, bastapodem ajudar no nosso dia a dia. Para explorar essa lista, basta
clicar no quarto ícone do menu lateral. A seguir você tem umaclicar no quarto ícone do menu lateral. A seguir você tem uma
imagem demonstrando alguns plugins que nós podemos baixar paraimagem demonstrando alguns plugins que nós podemos baixar para
trabalhar com TypeScript:trabalhar com TypeScript:
Figura 9.12: Visual Studio - plugins.Figura 9.12: Visual Studio - plugins.
Com isso, finalizamos mais este capítulo. No próximo, vamos iniciar Com isso, finalizamos mais este capítulo. No próximo, vamos iniciar
o desenvolvimento de uma API.o desenvolvimento de uma API.
C 10C 10
DDockocker: er: ConfigConfigurando urando ambieambiente nte de bde banco anco dede
dadosdados
Neste capítulo, configuraremos um Neste capítulo, configuraremos um ambiente de desenvolvimentoambiente de desenvolvimento
com Docker para a criação do nosso banco de dados.com Docker para a criação do nosso banco de dados.
A ideia aqui A ideia aqui não será aprofunnão será aprofundar no que é o dar no que é o Docker e nem em comoDocker e nem em como
ele funciona por baixo dos panos. ele funciona por baixo dos panos. Mas vamos passar Mas vamos passar rapidamenterapidamente
por alguns conceitos básicos, para que possamos montar o nossopor alguns conceitos básicos, para que possamos montar o nosso
ambiente de desenvolvimento.ambiente de desenvolvimento.
Por que eu escolhi o Docker? Inicialmente pensei em utilizar umaPor que eu escolhi o Docker? Inicialmente pensei em utilizar uma
plataforma pronta como o Azure com o Cosmos DB, mas plataforma pronta como o Azure com o Cosmos DB, mas comocomo
estamos criando uma aplicação do zero, estamos criando uma aplicação do zero, seria legal aproveitarmosseria legal aproveitarmos
para ver algo que está para ver algo que está sendo muito utilizado pela comunidade esendo muito utilizado pela comunidade e
requisitado por muitas empresas como conhecimento necessáriorequisitado por muitas empresas como conhecimento necessário
para processos seletivos.para processos seletivos.
Se você já Se você já conhece o Docker, pode pular para o final deste capítulo.conhece o Docker, pode pular para o final deste capítulo.
Agora, caso esse seja Agora, caso esse seja o seu primeiro cono seu primeiro contato com ele, acompatato com ele, acompanhe onhe o
passo a passo a seguir.passo a passo a seguir.
1010.1 .1 DDocockker er
O Docker é um projeto open source escrito na linguagem Go, queO Docker é um projeto open source escrito na linguagem Go, que
torna a criação e torna a criação e o gerenciamento de contêineres muito mais fácil.o gerenciamento de contêineres muito mais fácil.
Um contêiner (Um contêiner (container container ) é construído utilizando alguns recursos ) é construído utilizando alguns recursos dodo
kernel kernel , como namespaces, cgroups, chroot, que permitem que ele, como namespaces, cgroups, chroot, que permitem que ele
seja criado e rode como um processo isolado dentro do nossoseja criado e rode como um processo isolado dentro do nosso
Sistema Operacional.Sistema Operacional.
Atualmente, o D Atualmente, o Docker está dividido eocker está dividido em dois produtos,m dois produtos, CommunityCommunity
Edition (CE)Edition (CE) e e Enterprise Edition (EE)Enterprise Edition (EE). Como os nomes sugerem, a. Como os nomes sugerem, a
versão Community é gratuita e versão Community é gratuita e voltada para a comunidade,voltada para a comunidade,
enquanto a Enterprise é recomendada para uso empresarial. Para oenquanto a Enterprise é recomendada para uso empresarial. Para o
nosso exemplo, vamos utilizar a nosso exemplo, vamos utilizar a versão grátis, a Community.versão grátis, a Community.
O nosso primeiro passo para trabalhar com Docker será a criaçãoO nosso primeiro passo para trabalhar com Docker será a criação
de uma conta no de uma conta no portal Docker Hportal Docker Hub. Para isso, acesse ub. Para isso, acesse o linko link
https://hub.docker.com/signuphttps://hub.docker.com/signup e siga os passos e siga os passos necessários para onecessários para o
preenchimento do formulário de criação de uma nova preenchimento do formulário de criação de uma nova conta.conta.
Caso você já tenha uma conta criada, clique no linkCaso você já tenha uma conta criada, clique no link
https://id.docker.com/login/https://id.docker.com/login/ e acesse com o seu e acesse com o seu docker IDdocker ID e sua e sua
senhasenha. Com o passo da conta OK e já logado no portal, clique em. Com o passo da conta OK e já logado no portal, clique em
Download Download e instale-o no seu computador. e instale-o no seu computador.
Comandos básicosComandos básicos
Junto com o pacote de instalação do Docker vem uma CLI Junto com o pacote de instalação do Docker vem uma CLI (Interface(Interface
de Linha de Comando), que nós podemos acessar através de umde Linha de Comando), que nós podemos acessar através de um
terminal no nosso computador. Vamos conhecer alguns comandosterminal no nosso computador. Vamos conhecer alguns comandos
básicos que devem ajudar no seu dia a dia.básicos que devem ajudar no seu dia a dia.
Abra um terminal Abra um terminal no seu computador no seu computador e digite o segue digite o seguinte comandointe comando
nele:nele:
docker infodocker info
ResultadoResultado
Containers: 0Containers: 0
Running: 0Running: 0
Paused: 0Paused: 0
Stopped: 0Stopped: 0
Images: 0Images: 0
Server Version: 19.03.8Server Version: 19.03.8
Storage Driver: overlay2Storage Driver: overlay2
Backing Filesystem:Backing Filesystem: <<unknownunknown>>
Supports d_type: trueSupports d_type: true
Native Overlay Diff: trueNative Overlay Diff: true
Logging Driver: json-fileLogging Driver: json-file
Cgroup Driver: cgroupfsCgroup Driver: cgroupfs
Plugins:Plugins:
Volume: localVolume: local
Network: bridge host ipvlan macvlan null Network: bridge host ipvlan macvlan null overlayoverlay
Log: awslogs fluentd gcplogs gelf Log: awslogs fluentd gcplogs gelf journald json-file local logentriesjournald json-file local logentries
splunk syslogsplunk syslog
Swarm: inactiveSwarm: inactive
Runtimes: runcRuntimes: runc
Default Runtime: runcDefault Runtime: runc
Init Binary: docker-initInit Binary: docker-init
containerd containerd version: version: 7ad184331fa3e57ad184331fa3e55e52b890ea95e65e52b890ea95e65ba581ae34295ba581ae3429
runc runc version: version: dc9208a3303fedc9208a3303feef5b3839f4323def5b3839f4323d9beb36df0a9dd9beb36df0a9dd
init version: fec3683init version: fec3683
Security Options:Security Options:
seccompseccomp
Profile: defaultProfile: default
Kernel Version: Kernel Version: 4.19.76-linux4.19.76-linuxkitkit
Operating System: DockerDesktopOperating System: Docker Desktop
OSType: linuxOSType: linux
Architecture: x86_64Architecture: x86_64
CPUs: 2CPUs: 2
Total Memory: 1.945GiBTotal Memory: 1.945GiB
Name: Name: docker-desktopdocker-desktop
ID: ID: EPCH:EZQW:LGNEPCH:EZQW:LGNN:SWMC:RSAL:NYN:SWMC:RSAL:NYFO:QR4C:GM6L:64FO:QR4C:GM6L:646G:D2FX:A4CC:ZS6Q6G:D2FX:A4CC:ZS6Q
Docker Root Dir: Docker Root Dir: /var/lib/docke/var/lib/dockerr
Debug Mode: trueDebug Mode: true
File Descriptors: 38File Descriptors: 38
Goroutines: 49Goroutines: 49
System System Time: Time: 2020-08-29T202020-08-29T20:30:28.7428997:30:28.742899774Z74Z
EventsListenerEventsListeners: s: 33
Registry: Registry: https://index.https://index.docker.io/v1/docker.io/v1/
Labels:Labels:
Experimental: falseExperimental: false
Insecure Registries:Insecure Registries:
127.0.0.0/8127.0.0.0/8
Live Restore Enabled: falseLive Restore Enabled: false
Product License: Community EngineProduct License: Community Engine
Como você pode observar, esse comando retorna todas asComo você pode observar, esse comando retorna todas as
informações relacioinformações relacionadas ao Docker Hnadas ao Docker Host instalado nost instalado no nossoo nosso
computador, desde a versão instalada até a quantidade decomputador, desde a versão instalada até a quantidade de imagesimages
instaladas.instaladas.
Caso você precise de um comando rápido para ver a versão queCaso você precise de um comando rápido para ver a versão que
está instalada no seu ambiente, basta executar o está instalada no seu ambiente, basta executar o comandocomando dockerdocker
versionversion no seu terminal.no seu terminal.
ResultadoResultado
Client: Docker Engine - Client: Docker Engine - CommunityCommunity
Version: 19.03.8Version: 19.03.8
API API version: version: 1.401.40
Go Go version: version: go1.12.17go1.12.17
Git Git commit: commit: afacb8bafacb8b
Built: Built: Wed Wed Mar Mar 11 11 01:23:10 01:23:10 20202020
OS/Arch: windows/amd64OS/Arch: windows/amd64
Experimental: falseExperimental: false
Server: Docker Engine - Server: Docker Engine - CommunityCommunity
Engine:Engine:
Version: 19.03.8Version: 19.03.8
API API version: version: 1.40 1.40 (minimum (minimum version version 1.12)1.12)
Go Go version: version: go1.12.17go1.12.17
Git Git commit: commit: afacb8bafacb8b
Built: Built: Wed Wed Mar Mar 11 11 01:29:16 01:29:16 20202020
OS/Arch: linux/amd64OS/Arch: linux/amd64
Experimental: falseExperimental: false
containerd:containerd:
Version: v1.2.13Version: v1.2.13
GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc:runc:
Version: 1.0.0-rc10Version: 1.0.0-rc10
GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9ddGitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:docker-init:
Version: Version: 0.18.00.18.0
GitCommit: fec3683GitCommit: fec3683
Um ponto importante de se destacar nesse resultado é oUm ponto importante de se destacar nesse resultado é o OS/ArchOS/Arch ..
Note que eu estou em um ambiente Windows e, noNote que eu estou em um ambiente Windows e, no OS/ArchOS/Arch queque
está emestá em Server: Docker Engine - Server: Docker Engine - CommunityCommunity , está retornando, está retornando
linux/amd64linux/amd64 . Isso acontece porque eu estou em um ambiente. Isso acontece porque eu estou em um ambiente
Windows trabalhando com imagens Linux.Windows trabalhando com imagens Linux.
Isso é muito Isso é muito importante de se destacarimportante de se destacar, pois a , pois a maioria das imagensmaioria das imagens
disponíveis no Docker Hdisponíveis no Docker Hub foram criadas para rodar em ambiub foram criadas para rodar em ambienteente
Linux. Para utilizá-las em um ambiente com o SO Windows, nósLinux. Para utilizá-las em um ambiente com o SO Windows, nós
precisamos alterar oprecisamos alterar o OS/ArchOS/Arch conforme está no retorno anterior.conforme está no retorno anterior.
Fazer isso é bem simples, basta clicar na baleia, que está na suaFazer isso é bem simples, basta clicar na baleia, que está na sua
barra de tarefas com o botão direito do seu mouse, e embarra de tarefas com o botão direito do seu mouse, e em Switch toSwitch toLinux ContainersLinux Containers..
Falamos anteriormente de imagens em ambiente Linux e Windows,Falamos anteriormente de imagens em ambiente Linux e Windows,
mas não falamos o que seria uma imagem. Asmas não falamos o que seria uma imagem. As imagesimages Docker sãoDocker são
compostas por sistemas de arquivos em camadas que ficam umascompostas por sistemas de arquivos em camadas que ficam umas
sobre as outras. Elas são a base para a sobre as outras. Elas são a base para a construção de umaconstrução de uma
aplicação.aplicação.
Nós podemos criar as nossas imagens a partir de outras imagens eNós podemos criar as nossas imagens a partir de outras imagens e
versioná-las em algversioná-las em alguns portais, como Docker uns portais, como Docker HH ub ou Azureub ou Azure
Container Registry.Container Registry.
Para que esse passo fique mais claro, vamos criar um Para que esse passo fique mais claro, vamos criar um contêiner contêiner
utilizando uma imagem do MongoDB. Para isso, utilizando uma imagem do MongoDB. Para isso, execute o seguinteexecute o seguinte
comando no seu terminal:comando no seu terminal:
docker pull docker pull tutum/mongodbtutum/mongodb
O comandoO comando pullpull baixa uma imagem para o baixa uma imagem para o seu computadorseu computador. Para. Para
listar todas as imagens que listar todas as imagens que estão no seu computador, bastaestão no seu computador, basta
executar o comandoexecutar o comando docker imagesdocker images ..
ResultadoResultado
REPOSITORY REPOSITORY TAG TAG IMAGE IMAGE IDID
CREATED SIZECREATED SIZE
tutum/mongodb tutum/mongodb latest latest 64ca9521c70364ca9521c703
4 4 years years ago ago 503MB503MB
Com a imagem do Com a imagem do MongoDB no nosso computadorMongoDB no nosso computador, vamos criar , vamos criar umum
contêiner Docker para a nossa base de dados. Para isso, execute ocontêiner Docker para a nossa base de dados. Para isso, execute o
seguinte comando no seu terminal:seguinte comando no seu terminal:
docker run -d docker run -d -p 27017:27017 -p 28017:28017 -e -p 27017:27017 -p 28017:28017 -e AUTH=no tutum/mongodbAUTH=no tutum/mongodb
Analisando esse coma Analisando esse comando nós temos:ndo nós temos:
run run : esse comando é utilizado para a criação de um contêiner.: esse comando é utilizado para a criação de um contêiner.
-d-d
: esse parâmetro está : esse parâmetro está informando que o contêiner tem queinformando que o contêiner tem querodar em background.rodar em background.
-p-p : devemos utilizar esse : devemos utilizar esse parâmetro para especificar a portaparâmetro para especificar a porta
que vamos utilizar para conectar no que vamos utilizar para conectar no nosso contêinernosso contêiner. A porta. A porta
que está antes dosque está antes dos :: é a do nosso host e a é a do nosso host e a que está depois é aque está depois é a
que vamos utilizar para acessar que vamos utilizar para acessar o nosso contêiner.o nosso contêiner.
Para verificar se o Para verificar se o seu contêiner foi criado corretamente, basta abrir seu contêiner foi criado corretamente, basta abrir
um terminal no seu computador e executar o comandoum terminal no seu computador e executar o comando docker psdocker ps
nele.nele.
ResultadoResultado
CONTAINER CONTAINER ID ID IMAGE IMAGE COMMAND COMMAND CREATEDCREATED
STATUS PORTSSTATUS PORTS
NAMESNAMES
754b9c8b7e4f tutum/mongodb754b9c8b7e4f tutum/mongodb "/run.sh""/run.sh" 4 4 second second agoago
Up Up 4 4 seconds seconds 0.0.0.0:27017-0.0.0.0:27017->27017/tcp, >27017/tcp, 0.0.0.0:280170.0.0.0:28017->28017/tcp->28017/tcp
elastic_brahmaguptaelastic_brahmagupta
Esse comando lista os contêineres que estão em execução no seuEsse comando lista os contêineres que estão em execução no seu
computadorcomputador. Agora, para que possamosacessar . Agora, para que possamos acessar o MongoDB dentroo MongoDB dentro
do contêiner e criar uma nova base de dados, vamos utilizar umdo contêiner e criar uma nova base de dados, vamos utilizar um
cliente de acesso a banco de dados mongo, ocliente de acesso a banco de dados mongo, o Robo 3TRobo 3T ..
Caso você não o Caso você não o tenha instalado, segue link para download do seutenha instalado, segue link para download do seu
instalador:instalador: https://robomongo.org/https://robomongo.org/..
Com ele instalado no seu Com ele instalado no seu computadorcomputador, clique nos dois , clique nos dois monitoresmonitores
que ficam no canto que ficam no canto direito da ferramenta, em seguida, emdireito da ferramenta, em seguida, em createcreate ee
preencha os campos com os seguintes dados:preencha os campos com os seguintes dados:
Name Name : nome da sua conexão. Para esse exemplo vou utilizar : nome da sua conexão. Para esse exemplo vou utilizar
db_portal db_portal ..
Address Address : deixe os : deixe os dados defaultdados default localhostlocalhost na portana porta 2701727017 ..
Com os dados preenchidos, clique emCom os dados preenchidos, clique em savesave e depois em e depois em connect connect ..
Agora que estamo Agora que estamos conectados ao nosso ses conectados ao nosso servidor de banco drvidor de banco dee
dados, vamos criar uma nova base de dados chamadadados, vamos criar uma nova base de dados chamada db_portaldb_portal ..
Essa base será utilizada no Essa base será utilizada no próximo capítulo deste livro. Clique compróximo capítulo deste livro. Clique com
o botão direito emo botão direito em localhost localhost , em seguida, em, em seguida, em Create databaseCreate database e, no e, no
campocampo Database Name:Database Name:, preencha com o nome, preencha com o nome db_portaldb_portal . Agora. Agora
clique emclique em createcreate para que a base seja criada. para que a base seja criada.
Antes de darmos o Antes de darmos o próximo passo, é muito próximo passo, é muito importante todimportante todosos
estarmos familiarizados com alguns termos utilizados quandoestarmos familiarizados com alguns termos utilizados quando
trabalhamos com uma base de dados MongoDB. A seguir você temtrabalhamos com uma base de dados MongoDB. A seguir você tem
uma breve descrição de alguns uma breve descrição de alguns que utilizaremos neste livro:que utilizaremos neste livro:
databasedatabase: base de dados.: base de dados.
collectionscollections: para quem trabalha ou já teve contato com um: para quem trabalha ou já teve contato com um
banco de dados SQL, seriam as nossas tabelas, mas pensandobanco de dados SQL, seriam as nossas tabelas, mas pensando
em quem está começando na área, uma collection seria umaem quem está começando na área, uma collection seria uma
coleção de dados.coleção de dados.
fieldsfields: seriam as propriedades das nossas classes, que quando: seriam as propriedades das nossas classes, que quando
forem mapeadas para a nossa collection serão os campos ondeforem mapeadas para a nossa collection serão os campos onde
armazenaremos os valores.armazenaremos os valores.
Para finalizar este capítulo, vamos criar Para finalizar este capítulo, vamos criar uma nova collectionuma nova collection
chamadachamada newsnews dentro do nosso dentro do nosso databasedatabase db_portaldb_portal . Para isso,. Para isso,
clique com o botão direito emclique com o botão direito em CollectionsCollections e em e em Create CollectionCreate Collection..
Preencha o campoPreencha o campo Collection Name:Collection Name: com o valor com o valor newsnews ..
Com a criação do nosso contêiner MongoDB e a criação do nossoCom a criação do nosso contêiner MongoDB e a criação do nosso
databasedatabase db_portaldb_portal , nós podemos seguir para o próximo capítulo,, nós podemos seguir para o próximo capítulo,
onde daremos início à construção da nossa API.onde daremos início à construção da nossa API.
C 11C 11
CrianCriando APdo API TI TypeScypeScript, ript, Node.jNode.js, Ms, MongoDongoDB eB e
Docker Docker
11.1 Arquitetura básica do projeto11.1 Arquitetura básica do projeto
Neste capítulo, nós colocaremos em prática Neste capítulo, nós colocaremos em prática alguns conceitos quealguns conceitos que
aprendemos no decorrer do livro. Utilizaremos os aprendemos no decorrer do livro. Utilizaremos os conceitos deconceitos de
tipagem básica, interfaces e até Programação Orientada a Objetos.tipagem básica, interfaces e até Programação Orientada a Objetos.
Desenvolveremos uma API Desenvolveremos uma API utilizando as tecnologias TypeScriptutilizando as tecnologias TypeScript,,
Node.js, MongoDB e o Docker, que abordamos no capítulo anterior.Node.js, MongoDB e o Docker, que abordamos no capítulo anterior.
Para que você possa ter um exemplo real de como sPara que você possa ter um exemplo real de como se trabalhar come trabalhar comessas tecnologias, passaremos por todos os essas tecnologias, passaremos por todos os passos utilizados parapassos utilizados para
desenvolver uma API que retorna as últimas notícias do sitedesenvolver uma API que retorna as últimas notícias do site
Masterchef Brasil, em uma de suas edições.Masterchef Brasil, em uma de suas edições.
Criando os arquivos de configuração básicaCriando os arquivos de configuração básica
Já vamos começar direto partindo para a parte prática. NossoJá vamos começar direto partindo para a parte prática. Nosso
primeiro passo será abrir um terminal em seu diretório deprimeiro passo será abrir um terminal em seu diretório de
preferência e digitar o seguinte comando nele:preferência e digitar o seguinte comando nele: mkdir api_mcmkdir api_mc . Esse. Esse
comando deve criar um novo diretório com o nomecomando deve criar um novo diretório com o nome api_mcapi_mc ..
Conforme vimos no capítulo de introdução, para trabalhar comConforme vimos no capítulo de introdução, para trabalhar com
TTypeScript nós precisamos ypeScript nós precisamos criar o arquivocriar o arquivo tsconfig.jsontsconfig.json . Para isso,. Para isso,
execute o seguinte comando no seu execute o seguinte comando no seu terminal:terminal: tsc --inittsc --init ..
O comandoO comando tsc --inittsc --init cria o arquivocria o arquivo tsconfig.jsontsconfig.json com algumascom algumas
configurações padrões. Para que possamos desenvolver a nossaconfigurações padrões. Para que possamos desenvolver a nossa
API, será necessário API, será necessário fazer alguns ajustefazer alguns ajustes nesse arquivo. Abras nesse arquivo. Abra-o em-o em
seu editor de textos e atualize-o com o seguinte trecho de código:seu editor de textos e atualize-o com o seguinte trecho de código:
{{
"compilerOptions""compilerOptions": {: {
"target""target":: "es6""es6",,
"module""module":: "commonjs""commonjs",,
"esModuleInterop""esModuleInterop": true,: true,
"outDir""outDir":: "dist""dist",,
"typeRoots""typeRoots": [: [
"../node_modules/@types""../node_modules/@types"
],],
"types""types": [: [
"node""node"
]]
},},
"include""include": [: [
"**/*.ts""**/*.ts",,
"*.ts""*.ts",,
],],
"exclude""exclude": [: [
"node_modules""node_modules"
]]
}}
O que alteramos nele? Dentro deO que alteramos nele? Dentro de compilerOptionscompilerOptions nós passamos:nós passamos:
target target : versão do ECMAScript que vamos utilizar no projeto.: versão do ECMAScript que vamos utilizar no projeto.
module module : biblioteca que vamos utilizar para : biblioteca que vamos utilizar para trabalhar com ostrabalhar com os
módulos.módulos.
esModuleInteresModuleInteropop : permite importarmos os módulos: permite importarmos os módulos CommonJSCommonJS emem
conformidade com as especificações doconformidade com as especificações do target es6target es6 ..
outDir outDir : diretório de destino onde serão transpilados os : diretório de destino onde serão transpilados os nossosnossos
arquivos.arquivos.
typeRoots typeRoots : caminho dos pacotes: caminho dos pacotes@types@types que nós importaremosque nós importaremos
para trabalhar com Tpara trabalhar com TypeScript; ypeScript; veremos com mais detalhes noveremos com mais detalhes no
decorrer do capítulo.decorrer do capítulo.
Fora deFora de compilerOptionscompilerOptions , temos:, temos:
include include : local onde o compilador deve procurar os nossos: local onde o compilador deve procurar os nossos
arquivos para fazer oarquivos para fazer o transpiletranspile ;;
exclude exclude : os diretórios que devem ser ignorados no momento do: os diretórios que devem ser ignorados no momento do transpile transpile ..
Com o arquivoCom o arquivo tsconfig.jsontsconfig.json atualizado, vamos criar o nossoatualizado, vamos criar o nosso
arquivoarquivo package.jsonpackage.json . Digite o seguinte comando no . Digite o seguinte comando no seu terminal:seu terminal:
npm init -ynpm init -y ..
Caso esse seja o seu primeiro contato com esse arquivo, oCaso esse seja o seu primeiro contato com esse arquivo, o
package.jsonpackage.json armazena algumas informações do nosso projeto,armazena algumas informações do nosso projeto,
como nome, versão, descrição, o arquivo de inicialização e oscomo nome, versão, descrição, o arquivo de inicialização e os
pacotes que foram adicionados ao projeto.pacotes que foram adicionados ao projeto.
Com os arquivosCom os arquivos tsconfig.jsontsconfig.json ee package.jsonpackage.json criados, o próximocriados, o próximo
passo será a instalação dos pacotes necessários para opasso será a instalação dos pacotes necessários para o
desenvolvimento da nossa API. Para isso, desenvolvimento da nossa API. Para isso, execute os seguintesexecute os seguintes
comandos no seu terminal:comandos no seu terminal:
npm npm i i typescript typescript express express mongoose mongoose --save--save
npm npm i i @types/express @types/express @types/mongoo@types/mongoose se --save-dev--save-dev
Analisando esses pa Analisando esses pacotes, nós temos:cotes, nós temos:
typescript typescript : biblioteca necessária para que possamos informar a: biblioteca necessária para que possamos informar a
versão do TypeScript que está no nosso projeto.versão do TypeScript que está no nosso projeto.
express express : framework utilizado para desenvolvimento de aplicativo: framework utilizado para desenvolvimento de aplicativo
web do Node.js.web do Node.js.
mongoose mongoose : pacote necessário para que : pacote necessário para que possamos trabalhar compossamos trabalhar com
o banco de dados o banco de dados MongoDB.MongoDB.
Note que, na segunda linha, nós temos os mesmos pacotes, só queNote que, na segunda linha, nós temos os mesmos pacotes, só quecom o prefixocom o prefixo @type@type . Para quem não conhece esses pacotes, eles. Para quem não conhece esses pacotes, eles
são interfaces do TypeScript que nos permitem trabalhar com assão interfaces do TypeScript que nos permitem trabalhar com as
bibliotecas JavaScript. Como já sabemos, os bibliotecas JavaScript. Como já sabemos, os navegadores nãonavegadores não
interpretam códigos TypeScript, então precisamos dessas interpretam códigos TypeScript, então precisamos dessas interfacesinterfaces
para trabalhar no nosso ambiente de desenvolvimento.para trabalhar no nosso ambiente de desenvolvimento.
Para ficar mais claro, Para ficar mais claro, navegue até o diretórionavegue até o diretório
node_modules\@types\expressnode_modules\@types\express do seu projeto, abra o do seu projeto, abra o arquivoarquivo index.d.tsindex.d.ts
no seu editor de textos e note que dentro dele nós temos algumasno seu editor de textos e note que dentro dele nós temos algumas
interfaces:interfaces:
interfaceinterface Application extends core.Application { } Application extends core.Application { }
interfaceinterface CookieOptions extends core.CookieOptions { } CookieOptions extends core.CookieOptions { }
interfaceinterface Errback extends core.Errback { } Errback extends core.Errback { }
interfaceinterface ErrorRequestHandler<P = core.ParamsDictionary, ResBody = ErrorRequestHandler<P = core.ParamsDictionary, ResBody =
any, ReqBody = any, ReqQuery = core.Query>any, ReqBody = any, ReqQuery = core.Query>
extends core.ErrorRequestHaextends core.ErrorRequestHandler<P, ResBody, ReqBody, ndler<P, ResBody, ReqBody, ReqQuery> {ReqQuery> {
}}
interfaceinterface Express extends core.Express { } Express extends core.Express { }
interfaceinterface Handler extends core.Handler { } Handler extends core.Handler { }
interfaceinterface IRoute extends core.IRoute { } IRoute extends core.IRoute { }
interfaceinterface IRouter extends core.IRouter { } IRouter extends core.IRouter { }
interfaceinterface IRouterHandler<T> extends core.IRouterHandler<T> { } IRouterHandler<T> extends core.IRouterHandler<T> { }
interfaceinterface IRouterMatcher<T> extends core.IRouterMatcher<T> { } IRouterMatcher<T> extends core.IRouterMatcher<T> { }
interfaceinterface MediaType extends core.MediaType { } MediaType extends core.MediaType { }
interfaceinterface NextFunction extends core.NextFunction { } NextFunction extends core.NextFunction { }
interfaceinterface Request<P = Request<P = core.ParamsDicore.ParamsDictionary, ResBody = any, ctionary, ResBody = any, ReqBody =ReqBody =
any, ReqQuery = any, ReqQuery = core.Query> extends core.Request<P, ResBody, ReqBody,core.Query> extends core.Request<P, ResBody, ReqBody,
ReqQuery> { }ReqQuery> { }
interfaceinterface RequestHandler<P = core.ParamsDictionary, ResBody = any, RequestHandler<P = core.ParamsDictionary, ResBody = any,
ReqBody = ReqBody = any, ReqQuery = any, ReqQuery = core.Query> extends core.RequestHandlcore.Query> extends core.RequestHandler<P,er<P,
ResBody, ReqBody, ReqQuery> { }ResBody, ReqBody, ReqQuery> { }
interfaceinterface RequestParamHandler extends core.RequestParamHand RequestParamHandler extends core.RequestParamHandler { ler { }}
exportexport interfaceinterface Response<ResBody = any> Response<ResBody = any> extendsextends
core.Responsecore.Response<ResBody> { <ResBody> { }}
interfaceinterface Router extends core.Router { } Router extends core.Router { }
interfaceinterface Send extends core.Send { } Send extends core.Send { }
Caso você abra o pacoteCaso você abra o pacote expressexpress , que fica em, que fica em \node_modules\express\node_modules\express ,,
vai encontrar a implementação dessas interfaces em vai encontrar a implementação dessas interfaces em JavaScript.JavaScript.
Dando continuidade ao desenvolvimento do nosso Dando continuidade ao desenvolvimento do nosso projeto, paraprojeto, para
validar se todos pacotes foram validar se todos pacotes foram importados corretamente, abra o seuimportados corretamente, abra o seu
arquivoarquivo package.jsonpackage.json e observe se ele tem duas novas propriedades:e observe se ele tem duas novas propriedades:
uma chamadauma chamada dependenciesdependencies , para os pacotes, para os pacotes JavaScriptJavaScript que foramque foram
importados na primeira linha comimportados na primeira linha com --save--save , e outra chamada, e outra chamada
devDependenciesdevDependencies , para , para as interfaces TypeScript que foram importadasas interfaces TypeScript que foram importadas
utilizando o sufixoutilizando o sufixo --save-dev--save-dev ..
A seguir você tem o A seguir você tem o arquivoarquivo package.jsonpackage.json atualizado com asatualizado com as
dependências necessárias para o desenvolvimento da nossa API.dependências necessárias para o desenvolvimento da nossa API.
{{
"name""name":: "api_mc""api_mc",,
"version""version":: "1.0.0""1.0.0",,
"description""description":: """",,
"main""main":: "index.js""index.js",,
"scripts""scripts": {: {
"test""test":: "echo \"Error: no test specified\" && exit 1""echo \"Error: no test specified\" && exit 1"
},},
"keywords""keywords": [],: [],
"author""author":: """",,
"license""license":: "ISC""ISC",,
"dependencies""dependencies": {: {
"express""express":: "^4.17.1""^4.17.1",,
"mongoose""mongoose":: "^5.12.2""^5.12.2",,"typescript""typescript":: "^4.2.3""^4.2.3"
},},
"devDependencies""devDependencies": {: {
"@types/express""@types/express":: "^4.17.11""^4.17.11",,
"@types/mongoose""@types/mongoose":: "^5.10.4""^5.10.4"
}}
}}
Aproveitando q Aproveitando que estamos analisanue estamos analisando o arquivodo o arquivo package.jsonpackage.json ,,
vamos atualizar a parte devamos atualizar a parte de scriptsscripts com um trecho de código quecom um trecho de código que
vamos utilizar para executar a nossa API:vamos utilizar para executar a nossa API:
"scripts""scripts": {: {
"test""test":: "echo \"Error: no test specified\" && exit 1""echo \"Error: no test specified\" && exit 1",,
"compile""compile":: "tsc -w""tsc -w",,
"start""start":: "node "node ./dist/progra./dist/program.js"m.js"
},},
Nesse código, temos duas instruções:Nesse código, temos duas instruções:
compile compile : esse parâmetro já foi visto na introdução do livro. Ao: esse parâmetro já foi visto na introdução do livro. Ao
adicionar oadicionar o -w-w depois da instruçãodepois da instrução tsctsc , o compilador passa a, o compilador passa a
monitorar os arquivosmonitorar os arquivos .ts.ts do nosso projeto e, em caso dedo nosso projeto e, em caso de
alteração, ele faz o transpile dos alteração, ele faz o transpile dos nossos arquivos.nossos arquivos.
start start : aqui estamos passando qual será a instrução de: aqui estamos passando qual será a instrução deinicialização da nossa API.inicialização da nossa API.
Neste momento não se preocupe com o arquivoNeste momento não se preocupe com o arquivo program.jsprogram.js , nós o, nós o
criaremos nos próximos passos.criaremos nos próximos passos.
Criando a estrutura básica do projetoCriando a estrutura básica do projeto
Com os pacotes importados, criaremos agora a estrutura básica doCom os pacotes importados, criaremos agora a estrutura básica do
nosso projeto. Para isso, crie nosso projeto. Para isso, crie os seguintes diretórios:os seguintes diretórios:
models models : nesse diretório, nós criaremos as nossas models.: nesse diretório, nós criaremos as nossas models.
VVamos entender melhor o que amos entender melhor o que são essas classes são essas classes nos próximosnos próximos
passos.passos.
repository repository : aqui nós vamos mapear as nossas models com : aqui nós vamos mapear as nossas models com asas
nossas collections do MongoDB.nossas collections do MongoDB.
contracts contracts : nesse diretório, nós criaremos as nossas interfaces.: nesse diretório, nós criaremos as nossas interfaces.
services services : nesse diretório, nós criaremos a comunicação com o: nesse diretório, nós criaremos a comunicação com o
nosso banco de dados.nosso banco de dados.
controller controller : nesse diretório, nós criaremos a entrada do nosso: nesse diretório, nós criaremos a entrada do nosso
projeto.projeto.
infra infra : nesse diretório, nós criaremos alguns : nesse diretório, nós criaremos alguns arquivos dearquivos de
configuração, como um arquivo de conexão com banco deconfiguração, como um arquivo de conexão com banco de
dados.dados.
Com a estrutura básica criada e os pacotes necessários importados,Com a estrutura básica criada e os pacotes necessários importados,
vamos iniciar o desenvolvimento da nossa API.vamos iniciar o desenvolvimento da nossa API.
111.2 D1.2 Desenesenvolvivolvimento da APmento da APII
O primeiro passo para o desenvolvimento de um projeto é aO primeiro passo para o desenvolvimento de um projeto é a
definição de suas models. As models são a representação de umdefinição de suas models. As models são a representação de um
conjunto de informações sobre determinado conceito do sistema.conjunto de informações sobre determinado conceito do sistema.
Toda model possui atributos, que são as informações que aToda model possui atributos, que são as informações que a
referenciam.referenciam.
Para ficar mais claro, vamos voltar ao capítulo 4 sobre ProgramaçãoPara ficar mais claro, vamos voltar ao capítulo 4 sobre Programação
Orientada a Objetos. Você se lembra da classeOrientada a Objetos. Você se lembra da classe ContaConta ? ? Ela poEla pode ser de ser
vista como uma model ou entidade, como é conhecida navista como uma model ou entidade, como é conhecida na
modelagem de software, e tem os modelagem de software, e tem os seguintes atributos:seguintes atributos: numeroDaContanumeroDaConta ,,
titulartitular ee saldosaldo . A definição das models é um conceito básico,. A definição das models é um conceito básico,
porém muito importante na modelagem de um porém muito importante na modelagem de um software.software.
Agora que sabe Agora que sabemos o que é uma modmos o que é uma model, vamos criar uma pael, vamos criar uma para ara a
nossa listagem de notícias. Para isso, nós precisamos responder ànossa listagem de notícias. Para isso, nós precisamos responder à
seguinte pergunta:seguinte pergunta: O que toda notícia tem?O que toda notícia tem? Em uma breve análise Em uma breve análise
nós temos: título, nós temos: título, chapéu, texto, autor, imagem, data de publicação,chapéu, texto, autor, imagem, data de publicação,
tags, um link para a notícia e um atributo para dizer se ela está tags, um link para a notícia e um atributo para dizer se ela está ativaativa
ou não.ou não.
Com a definição dos atributos da nossa model, o próximo passoCom a definição dos atributos da nossa model, o próximo passo
será criá-la. Crie um será criá-la. Crie um novo arquivo chamadonovo arquivo chamado newsSchema.tsnewsSchema.ts dentro dodentro do
diretóriodiretório modelsmodels e atualize-o com o e atualize-o com o seguinte trecho de código:seguinte trecho de código:
importimport mongoose from mongoose from "mongoose""mongoose";;
exportexport constconst NewsSchema = NewsSchema = newnew mongoose.Schemmongoose.Schema({a({
titulo: { type:titulo: { type: StringString }, },
chapeu: { type:chapeu: { type: StringString }, },
texto: { type:texto: { type: StringString }, },
autor: { type:autor: { type: StringString }, },
imagem: { type:imagem: { type: StringString }, },
dataPublicacaodataPublicacao: { : { type:type: DateDate }, },
tags: { type:tags: { type: StringString }, },
link: { type:link: { type: StringString }, },
ativo: { type:ativo: { type: BooleanBoolean } }
});});
O código está simples, O código está simples, estamos exportando uma constanteestamos exportando uma constante
chamadachamada NewsSchemaNewsSchema , que está instanciando, que está instanciando SchemaSchema do pacotedo pacote
mongoosemongoose , em seguida, passamos os atributos, que nós mapeamos, em seguida, passamos os atributos, que nós mapeamos
no passo anteriorno passo anterior, para a c, para a criação da nossa model.riação da nossa model.
ComCom NewsSchemaNewsSchema criada, o próximo passo será mapeá-la com ocriada, o próximo passo será mapeá-la com o
nosso banco de dados. Para isso, nós precisamos criar o nossonosso banco de dados. Para isso, nós precisamos criar o nosso
repositoryrepository ..
Como vimos anteriormente, oComo vimos anteriormente, o repositoryrepository é responsável por mapear é responsável por mapear
as nossas models com o banco de as nossas models com o banco de dados. Como estamos utilizandodados. Como estamos utilizando
MongoDB, a nossa classeMongoDB, a nossa classe NewsRepositoryNewsRepository vai mapear a nossa classevai mapear a nossa classe
NewsSchemaNewsSchema com uma collection.com uma collection.
Para ficar mais claro, crie um novo arquivo chamadoPara ficar mais claro, crie um novo arquivo chamado
newsRepository.tsnewsRepository.ts dentro do diretóriodentro do diretório repositoryrepository e atualize-o com oe atualize-o com o
seguinte trecho de código:seguinte trecho de código:
importimport mongoose from mongoose from "mongoose""mongoose";;
importimport { NewsSchema } from { NewsSchema } from "../models/newsSchema""../models/newsSchema";;
exportexport constconst NewsRepository = NewsRepository = mongoose.modelmongoose.model(("news""news", NewsSchema);,NewsSchema);
Em um breve resumo, estamos dizendo ao pacoteEm um breve resumo, estamos dizendo ao pacote mongoosemongoose que eleque ele
deve criar uma collection chamadadeve criar uma collection chamada newsnews , com os dados nossa, com os dados nossa
modelmodel NewsSchemaNewsSchema ..
Trabalhando com interfacesTrabalhando com interfaces
Com o mapeamento OK, o próximo passo será a criação de umCom o mapeamento OK, o próximo passo será a criação de um
arquivo de contrato, algo que diga o que os nossos serviços devemarquivo de contrato, algo que diga o que os nossos serviços devem
ter. Voltando ao capítulo 5, sobre interfaces, sabemos que esse éter. Voltando ao capítulo 5, sobre interfaces, sabemos que esse é
um dos papéis de uma interface.um dos papéis de uma interface.
Crie um arquivo chamadoCrie um arquivo chamado iNewsService.tsiNewsService.ts dentro do diretóriodentro do diretório
contractscontracts e atualize-o com o e atualize-o com o seguinte trecho de código:seguinte trecho de código:
importimport { Result } from { Result } from "../infra/result""../infra/result";;
exportexport interfaceinterface INewsService { INewsService {
getget(id:(id: stringstring););
getAll(page:getAll(page: numbernumber, qtd:, qtd: numbernumber): ): Promise<ResultPromise<Result>;>;
}}
Na interfaceNa interface INewsServiceINewsService , nós temos dois métodos: um com, nós temos dois métodos: um com
parâmetroparâmetro IdId recebendo um número e um recebendo um número e um outro recebendo doisoutro recebendo dois
parâmetros,parâmetros, pagepage ee qtdqtd , retornando uma, retornando uma promisepromise dede ResultResult (classe(classe
que vamos criar a seguir).que vamos criar a seguir).
Para quem não conhece umaPara quem não conhece uma promisepromise , como o próprio nome diz,, como o próprio nome diz,
umauma promisepromise é uma promessa. A é uma promessa. A sua ideia principal é representar sua ideia principal é representar
fluxos assíncronos de forma sequencial, além de fluxos assíncronos de forma sequencial, além de favorecer ofavorecer o
tratamento de exceções.tratamento de exceções.
A seguir você tem os e A seguir você tem os estados que nós podstados que nós podemos utilizar quanemos utilizar quandodo
trabalhamos com um objetotrabalhamos com um objeto promisespromises ::
Resolve Resolve : quando ela retorna o valor : quando ela retorna o valor esperado.esperado.
Reject Reject : no caso de algum erro, ela retorna que essa chamada: no caso de algum erro, ela retorna que essa chamada
foi rejeitada.foi rejeitada.
Pending Pending : quando ela ainda está esperando o valor ou o: quando ela ainda está esperando o valor ou o
resultado final da requisição.resultado final da requisição.
Agora para resol Agora para resolver o erro de refever o erro de referência que está drência que está dando na interando na interfaceface
INewsServiceINewsService , vamos criar a classe, vamos criar a classe ResultResult . Para isso, crie um novo. Para isso, crie um novo
arquivo chamadoarquivo chamado result.tsresult.ts dentro do diretóriodentro do diretório infrainfra e atualize-oe atualize-o
com o seguinte trecho de código:com o seguinte trecho de código:
exportexport classclass Result { Result {
Qtd:Qtd: numbernumber;;
Page:Page: numbernumber;;
Total:Total: numbernumber;;
Data:Data: anyany
}}
A utilização da cl A utilização da classeasse ResultResult é uma boa prática no é uma boa prática no desenvolvimentodesenvolvimento
de APIs. Ela nos permite ter um padrão de retorno como: qual é ade APIs. Ela nos permite ter um padrão de retorno como: qual é a
quantidade de dados que estamos quantidade de dados que estamos retornando,retornando, QtdQtd , a página em, a página em
que estamos (pensando em listagem de que estamos (pensando em listagem de resultados),resultados), PagePage , o total de, o total de
registros que nós temos salvos no nosso banco de dados para asregistros que nós temos salvos no nosso banco de dados para as
próximas páginas,próximas páginas, TotalTotal , e o resultado, em, e o resultado, em DataData ..
Criando as nossas pesquisasCriando as nossas pesquisas
Agora que nós cri Agora que nós criamos o nosso contrato, amos o nosso contrato, o próximo passo será o próximo passo será aa
criação de uma classe para o implementarmos. Para isso, crie umcriação de uma classe para o implementarmos. Para isso, crie um
novo arquivo chamadonovo arquivo chamado newsService.tsnewsService.ts dentro do diretóriodentro do diretório servicesservices ee
atualize-o com o seguinte trecho de atualize-o com o seguinte trecho de código:código:
importimport { INewsService } from { INewsService } from "../contracts/iNewsService""../contracts/iNewsService";;
importimport { Result } from { Result } from "../infra/result""../infra/result";;
importimport { NewsRepository } from { NewsRepository } from "../repository/newsRepository""../repository/newsRepository";;
exportexport classclass NewsService NewsService implementsimplements INewsService { INewsService {
asyncasync getget(_id:(_id: stringstring) {) {
letlet result result = await = await NewsRepository.NewsRepository.findById(_id);findById(_id);
returnreturn result; result;
}}
async getAll(page:async getAll(page: numbernumber, qtd:, qtd: numbernumber): Promise<Result> {): Promise<Result> {
letlet result = result = newnew Result(); Result();
result.Page = page;result.Page = page;
result.Qtd = qtd;result.Qtd = qtd;
result.Total = result.Total = await NewsRepository.count({});await NewsRepository.count({});
result.Data = await result.Data = await NewsRepositoryNewsRepository.find({}).skip.find({}).skip((page * ((page * qtd) -qtd) -
qtd).limit(qtd);qtd).limit(qtd);
returnreturn result; result;
}}
}}
Analisando o t Analisando o trecho de código, nrecho de código, nós temos:ós temos:
No início do arquivo, nós No início do arquivo, nós estamos implementando a interfaceestamos implementando a interface
INewsService INewsService e importando a classee importando a classe NewsRepositoryNewsRepository . Como vimos. Como vimos
anteriormente, essa classe é responsável pela integração comanteriormente, essa classe é responsável pela integração com
o nosso banco de dados.o nosso banco de dados.
Em seguida, nós estamos Em seguida, nós estamos implementando os métodos queimplementando os métodos que
estão no estão no contrato/interfacontrato/interfacece INewsServiceINewsService ..
Note que temos duas novas palavras reservadas no trecho emNote que temos duas novas palavras reservadas no trecho em
NewsServiceNewsService , a, a asyncasync e ae a awaitawait . Para quem não as conhece, quando. Para quem não as conhece, quando
declaramos uma função comdeclaramos uma função com asyncasync , estamos passando que essa, estamos passando que essa
çç yy , p q, p q
função é assíncrona e que função é assíncrona e que ela não deve bloquear o sela não deve bloquear o segmentoegmento
principal do nosso código. A palavraprincipal do nosso código. A palavra awaitawait é utilizada dentro de umaé utilizada dentro de uma
funçãofunção asyncasync , então adicioná-la faz com que o fluxo da função, então adicioná-la faz com que o fluxo da função
assíncrona não seja interrompido esperando pelo retorno da função.assíncrona não seja interrompido esperando pelo retorno da função.
Criando a nossa Controller Criando a nossa Controller
Agora que nós j Agora que nós já implementamos o á implementamos o nosso contrato, o prnosso contrato, o próximo passoóximo passo
será a criação de um arquivo de entrada do nosso projeto. Paraserá a criação de um arquivo de entrada do nosso projeto. Para
isso, crie um novo arquivo chamadoisso, crie um novo arquivo chamado newsController.tsnewsController.ts dentro dodentro do
diretóriodiretório controllercontroller e atualize-o com o e atualize-o com o seguinte trecho de código:seguinte trecho de código:
importimport { NewsService } from { NewsService } from "../services/newsService""../services/newsService";;
importimport { Request,Response } { Request, Response } fromfrom "express""express";;
classclass NewsController { NewsController {
privateprivate _service: _service: NewsService;NewsService;
constructorconstructor() {() {
thisthis._service =._service = newnew NewsService();NewsService();
}}
asyncasync getget(request: Request, response: Response) {(request: Request, response: Response) {
trytry { {
constconst page = page = request.paramsrequest.params.page ?.page ?parseIntparseInt(request.params(request.params.page) : .page) : 1;1;
constconst qtd = qtd = request.paramrequest.params.qtd ?s.qtd ? parseIntparseInt(request.params.qtd)(request.params.qtd)
: 10;: 10;
letlet result = await result = await thisthis._service.getA._service.getAll(page, ll(page, qtd);qtd);
response.statresponse.status(200).json({ result us(200).json({ result });});
}} catchcatch (error) { (error) {
response.statresponse.status(500).json({ error: us(500).json({ error: error.message ||error.message ||
error.toStrinerror.toString() g() });});
}}
}}
async getById(request: Request, response: Response) {async getById(request: Request, response: Response) {
trytry { {
constconst _id _id = request.params.id;= request.params.id;
letlet result = await result = await thisthis._service.get(_id);._service.get(_id);
response.statresponse.status(200).json({ result us(200).json({ result });});
}} catchcatch (error) { (error) {
response.statresponse.status(500).json({ error: us(500).json({ error: error.message ||error.message ||
error.toStrinerror.toString() g() });});
}}
}}
}}
exportexport defaultdefault newnew NewsControlleNewsController();r();
Analisando a n Analisando a nossa classeossa classe NewsControllerNewsController nós temos:nós temos:
No início do arquivo nós estamos importando a classeNo início do arquivo nós estamos importando a classe NewsService NewsService e instanciando-a dentro doe instanciando-a dentro do construtorconstrutor ..
Em seguida, nós criamos dois métodosEm seguida, nós criamos dois métodos asyncasync , um para, um para
consulta porconsulta por _id_id e um outro para e um outro para retornar uma listagemretornar uma listagem
paginada.paginada.
Note que nós temos duas novas palavras reservadas nessa classe,Note que nós temos duas novas palavras reservadas nessa classe,
aa trytry e ae a catchcatch . Para quem está tendo o seu primeiro contato com. Para quem está tendo o seu primeiro contato com
elas neste livro, nós elas neste livro, nós utilizamosutilizamos try/catchtry/catch para tratamento depara tratamento de
exceções:exceções:
try try : consegue recuperar erros que possam : consegue recuperar erros que possam ocorrer no código.ocorrer no código.
catch catch : faz o : faz o tratamento dos erros que aconteceram, retornatratamento dos erros que aconteceram, retorna
umauma exceptionexception que pode ser tratada e que pode ser tratada e retornada noretornada no responseresponse ..
Com o arquivo de entrada criado, o próximo passo será a criação deCom o arquivo de entrada criado, o próximo passo será a criação de
um arquivo de configuração para conexão com o banco de dados.um arquivo de configuração para conexão com o banco de dados.
Para isso, crie um novo arquivo chamadoPara isso, crie um novo arquivo chamado db.tsdb.ts dentro do diretóriodentro do diretório
infrainfra e atualize-o com o e atualize-o com o seguinte trecho de código:seguinte trecho de código:
importimport mongoose from mongoose from 'mongoose''mongoose';;
classclass Database { Database {
privateprivate DB_URL = DB_URL = "mongodb://localhost:27017/db_portal""mongodb://localhost:27017/db_portal";;
pp __ g _pg _p
createConnecticreateConnection() on() {{
mongoose.connect(mongoose.connect(thisthis.DB_URL);.DB_URL);
}}
}}
exportexport defaultdefault Database; Database;
Nesse arquivo, estamos passando a string de conexão do nossoNesse arquivo, estamos passando a string de conexão do nosso
banco de dados para o banco de dados para o métodométodo connectconnect dodo mongoosemongoose . Ele se. Ele se
encarregará de conectar com o banco de dados e criar as nossasencarregará de conectar com o banco de dados e criar as nossas
transações.transações.
11.3 Arquivo de inicialização do projeto11.3 Arquivo de inicialização do projeto
Agora que nós j Agora que nós já temos a estrutura á temos a estrutura básica do nosso projbásica do nosso projeto, oeto, o
próximo passo será a próximo passo será a criação do arquivo de inicialização.criação do arquivo de inicialização.
Existem algumas formas de criar esse arquivo. Eu já trabalhei emExistem algumas formas de criar esse arquivo. Eu já trabalhei em
alguns projetos nos quais o time optou por deixar tudo em umalguns projetos nos quais o time optou por deixar tudo em um
arquivo chamadoarquivo chamado app.tsapp.ts , trabalhamos com essa , trabalhamos com essa arquitetura por umarquitetura por um
tempo, mas depois de estudar outras arquiteturas, como a do .NETtempo, mas depois de estudar outras arquiteturas, como a do .NET
Core, eu resolvi separar esse Core, eu resolvi separar esse arquivo de inicialização em doisarquivo de inicialização em dois
arquivos: um chamadoarquivos: um chamado startUp.tsstartUp.ts e um outro chamadoe um outro chamado program.tsprogram.ts ..
Começando pelo arquivoComeçando pelo arquivo startUp.tsstartUp.ts , esse seria o arquivo de, esse seria o arquivo de
configurações do nosso projeto. É nele que configurações do nosso projeto. É nele que devemos inicializar odevemos inicializar o
expressexpress , o nosso arquivo de conexão com o banco de , o nosso arquivo de conexão com o banco de dadosdados db.tsdb.ts
e a nossa classee a nossa classe NewsControllerNewsController . Para que você possa ter um . Para que você possa ter um melhor melhor
entendimento, vou separar a implementação da classeentendimento, vou separar a implementação da classe startUp.tsstartUp.ts
em algumas partes e explicar cada uma delas.em algumas partes e explicar cada uma delas.
O primeiro passo será importar os pacotes necessários para asO primeiro passo será importar os pacotes necessários para as
nossas configurações:nossas configurações:
importimport express, { Application, Request, Response } from express, { Application, Request, Response } from "express""express";;
importimport database from database from "./infra/db""./infra/db";;
importimport NewsController from NewsController from "./controller/newsController""./controller/newsController";;
VVoltando ao capítulo 4 sobre Orientação a Objetos, oltando ao capítulo 4 sobre Orientação a Objetos, vamos criar umavamos criar uma
nova classe chamadanova classe chamada StartUpStartUp com umcom um construtorconstrutor vazio e umvazio e um
método chamadométodo chamado routesroutes para que possamos criar as rotas depara que possamos criar as rotas de
entrada do nosso projeto.entrada do nosso projeto.
Não se preocupe com oNão se preocupe com o construtorconstrutor nesse momento, nós vamosnesse momento, nós vamos
atualizá-lo nos próximos passos.atualizá-lo nos próximos passos.
classclass StartUp { StartUp {
constructorconstructor() { }() { }
routes() { }routes() { }
}}
Agora crie duas n Agora crie duas novas propriedadeovas propriedades, umas, uma publicpublic , para trabalhar , para trabalhar
com a biblioteca docom a biblioteca do expressexpress , e uma outra, e uma outra privateprivate , para conexão, para conexão
com o banco de dados:com o banco de dados:
classclass StartUp { StartUp {
publicpublic app: Application; app: Application;
privateprivate _db: database = _db: database = newnew database(); database();
//outras implementações//outras implementações
EmEm appapp , nós estamos passando, nós estamos passando ApplicationApplication do pacotedo pacote expressexpress e,e,emem _db_db , estamos instanciando a nossa classe, estamos instanciando a nossa classe databasedatabase , para que, para que
possamos conectar o projeto ao nosso banco de dados. Com apossamos conectar o projeto ao nosso banco de dados.Com a
configuração doconfiguração do expressexpress e a conexão à nossa base de dados OK,e a conexão à nossa base de dados OK,
vamos criar as rotas de entrada do nosso projeto:vamos criar as rotas de entrada do nosso projeto:
classclass StartUp { StartUp {
//outras implementações//outras implementações
routes() {routes() {
thisthis.app.route(.app.route("/""/").get((req, res) => {).get((req, res) => {
res.send({ versao:res.send({ versao: "0.0.1""0.0.1" }); });
}); });
thisthis.app.route(.app.route("/api/v1/news/:page/:qtd""/api/v1/news/:page/:qtd").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn NewsControlleNewsController.get(req, res);r.get(req, res);
});});
thisthis.app.route(.app.route("/api/v1/news/:id""/api/v1/news/:id").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn NewsControlleNewsController.getById(req, res);r.getById(req, res);
});});
}}
//outras //outras implementaçõeimplementaçõess
}}
Analisando o t Analisando o trecho de código arecho de código acima nós temos:cima nós temos:
EmEm this.app.route("/")this.app.route("/") , nós estamos criando uma rota para, nós estamos criando uma rota para
testar a nossa API. O pessoal da comunidade chama essa rotatestar a nossa API. O pessoal da comunidade chama essa rota
dede Health CheckHealth Check , ela geralmente é utilizada com outros serviços,, ela geralmente é utilizada com outros serviços,
comocomo zabbixzabbix ,, application insightsapplication insights para monitorar se a API estápara monitorar se a API está
funcionando corretamente.funcionando corretamente.
Em seguida, estamos criando duas novas rotas que devemEm seguida, estamos criando duas novas rotas que devem
chamar os dois métodos da nossachamar os dois métodos da nossa NewsControllerNewsController , o, o getget e oe o
getById getById ..
Agora atualize Agora atualize o seuo seu construtorconstrutor com o seguinte trecho de com o seguinte trecho de código:código:
constructorconstructor() {() {
thisthis.app = express();.app = express();
thisthis._db.createConnection();._db.createConnection();
thisthis.routes();.routes();
}}
E para finalizar a configuração da cE para finalizar a configuração da classelasse StartUpStartUp , nós devemos, nós devemos
exportá-la para que possamos chamá-la no nosso exportá-la para que possamos chamá-la no nosso arquivoarquivo
program.tsprogram.ts . Para isso, adicione o . Para isso, adicione o seguinte trecho de código no finalseguinte trecho de código no final
do seu arquivodo seu arquivo startUp.tsstartUp.ts ::
exportexport defaultdefault newnew StartUp(); StartUp();
A seguir você tem a A seguir você tem a classeclasse StartUpStartUp completa:completa:
importimport express, { Application, Request, Response } from express, { Application, Request, Response } from "express""express";;
importimport database from database from "./infra/db""./infra/db";;importimport NewsController from NewsController from "./controller/newsController""./controller/newsController";;
classclass StartUp { StartUp {
publicpublic app: Application; app: Application;
privateprivate _db: database = _db: database = newnew database(); database();
constructorconstructor() {() {
thisthis.app = express();.app = express();
thisthis._db.createConnection();._db.createConnection();
thisthis.routes();.routes();
}}
routes() {routes() {
thisthis.app.route(.app.route("/""/").get((req, res) => {).get((req, res) => {
res.send({ versao:res.send({ versao: "0.0.1""0.0.1" }); });
});});
thisthis.app.route(.app.route("/api/v1/news/:page/:qtd""/api/v1/news/:page/:qtd").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn NewsControlleNewsController.get(req, res);r.get(req, res);
});});
thisthis.app.route(.app.route("/api/v1/news/:id""/api/v1/news/:id").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn NewsControlleNewsController.getById(req, res);r.getById(req, res);
});});
}}
}}
exportexport defaultdefault newnew StartUp(); StartUp();
Com a finalização da classeCom a finalização da classe StartUpStartUp , crie o seu arquivo, crie o seu arquivo program.tsprogram.ts
na raiz do seu projeto e atualize-o com o seguinte trecho de código:na raiz do seu projeto e atualize-o com o seguinte trecho de código:
importimport StartUp from StartUp from "./startUp""./startUp";;
letlet port = port = "5000""5000";;
StartUp.app.listen(port,StartUp.app.listen(port, functionfunction ()() {{
consoleconsole.log(`servidor rodando na porta: .log(`servidor rodando na porta: ${port}`);${port}`);
});});
Analisando o a Analisando o arquivorquivo program.tsprogram.ts nós temos:nós temos:
Na primeira linha, estamos importando a nossa Na primeira linha, estamos importando a nossa classeclasse StartUpStartUp ..
Em seguida, estamos passando a porta em que oEm seguida, estamos passando a porta em que o expressexpress devedeve
executar a nossa aplicação.executar a nossa aplicação.
Agora para qu Agora para que possamos validar se tue possamos validar se tudo está configurado está configuradodo
corretamente, execute o comandocorretamente, execute o comando npm run compilenpm run compile no seu terminal.no seu terminal.
VVocê se lembra dessa instrução? ocê se lembra dessa instrução? Nós a adicionamos ao arquivoNós a adicionamos ao arquivo
package.jsonpackage.json e vimos como ele funciona no capítulo de introdução doe vimos como ele funciona no capítulo de introdução do
livro:livro:
"scripts""scripts": {: {
//outras //outras implementaçoeimplementaçoess
"compile""compile":: "tsc -w""tsc -w",,
},},
ResultadoResultado
> tsc -w> tsc -w
Starting compilationStarting compilation inin watch mode... watch mode...
Found 0 errors. Found 0 errors. WatchingWatching forfor file changes. file changes.
11.4 Incremental flag11.4 Incremental flag
Um ponto importante de se destacar nesse momento é que à versãoUm ponto importante de se destacar nesse momento é que à versão
3.43.4 do do TTypeScript foi adicionada uma ypeScript foi adicionada uma funcionalidade chamadafuncionalidade chamada
Incremental flagIncremental flag , que pode ser , que pode ser utilizada no seu compiladorutilizada no seu compilador. Essa. Essanova funcionalidade salva as nova funcionalidade salva as últimas alterações do cúltimas alterações do compiladorompilador,,
fazendo com que ele seja mais rápido no momento defazendo com que ele seja mais rápido no momento de
desenvolvimento. Para utilizar essa funcionalidade, basta adicioná-desenvolvimento. Para utilizar essa funcionalidade, basta adicioná-
la dentro dela dentro de compilerOptionscompilerOptions no seu arquivono seu arquivo tsconfig.jsontsconfig.json ..
"compilerOptions""compilerOptions": {: {
//outras implementaçoes//outras implementaçoes
"incremental""incremental": true,: true,
Mas antes de atualizar o seu arquivoMas antes de atualizar o seu arquivo tsconfig.jsontsconfig.json , vamos passar , vamos passar
um outro parâmetro para o nosso um outro parâmetro para o nosso compiladorcompilador, o, o -diagnostics-diagnostics ..
"scripts""scripts": {: {
//outras implementaçoes//outras implementaçoes
"compile""compile":: "tsc "tsc -w -w -diagnostics"-diagnostics",,
Esse parâmetro deve retornar no seu Esse parâmetro deve retornar no seu terminal um diagnósticoterminal um diagnóstico
completo, como quanto de memória foi utilizado para completo, como quanto de memória foi utilizado para o transpile,o transpile,CPU etc. Com o arquivoCPU etc. Com o arquivo package.jsonpackage.json atualizado, execute oatualizado, execute o
comandocomando npm run compilenpm run compile novamente no seu terminal.novamente no seu terminal.
//Resultado//Resultado
Files: 99Files: 99
Lines: 53010Lines: 53010
Nodes: 225351Nodes: 225351
Identifiers: 80391Identifiers: 80391
Symbols: 90546Symbols: 90546
Types: 33794Types: 33794
Instantiations: 104249Instantiations:104249
Memory Memory used: used: 137637K137637K
I/O I/O read: read: 0.35s0.35s
I/O I/O write: write: 0.06s0.06s
Parse Parse time: time: 2.16s2.16s
Bind Bind time: time: 0.85s0.85s
Check Check time: time: 3.98s3.98s
Emit Emit time: time: 0.25s0.25s
Total Total time: time: 7.23s7.23s
Agora, para qu Agora, para que possamos comparar como e possamos comparar como o compilador doo compilador do
TypeScript ficou antes e depois da funcionalidadeTypeScript ficou antes e depois da funcionalidade Incremental flagIncremental flag ,,adicione-a ao seu arquivoadicione-a ao seu arquivo tsconfig.jsontsconfig.json , em seguida, altere, em seguida, altere
qualquer arquivo no seu projeto e rode qualquer arquivo no seu projeto e rode o comandoo comando npm run compilenpm run compile
novamente:novamente:
//Resultado//Resultado
Files: 99Files: 99
Lines: 52955Lines: 52955
Nodes: 226127Nodes: 226127
Identifiers: 80635Identifiers: 80635
Symbols: 52682Symbols: 52682
Types: 78Types: 78
Instantiations: 0Instantiations: 0
Memory Memory used: used: 144461K144461K
I/O I/O read: read: 0.00s0.00s
I/O I/O write: write: 0.00s0.00s
Parse Parse time: time: 0.00s0.00s
Bind Bind time: time: 0.00s0.00s
Check Check time: time: 0.00s0.00s
Emit Emit time: time: 0.00s0.00s
Total Total time: time: 0.01s0.01s
Note a diferença: no primeiro exemplo, sem Note a diferença: no primeiro exemplo, sem aa Incremental flagIncremental flag , nós, nós
tivemostivemos 7.23s7.23s no tempo do transpile e, no segundo, tivemos no tempo do transpile e, no segundo, tivemos 0.01s0.01s..
Bem mais rápido, não?Bem mais rápido, não?
E agora, para que possamos E agora, para que possamos validar o desenvolvimento dos passosvalidar o desenvolvimento dos passos
anteriores, execute o comandoanteriores, execute o comando npm startnpm start no seu terminal.no seu terminal.
ResultadoResultado
node node ./dist/progra./dist/program.jsm.js
servidor rodando na porta: 5000servidor rodando na porta: 5000
Abra o seguinte Abra o seguinte endereço no seu endereço no seu navegador:navegador: http://localhost:5000http://localhost:5000
ResultadoResultado
{{
"versao""versao":: "0.0.1""0.0.1"
}}
Nessa chamada, nós estamos requisitando a Nessa chamada, nós estamos requisitando a nossa rotanossa rota defaultdefault , a, a
rota derota de health checkhealth check . Como nossa API está funcionando. Como nossa API está funcionando
corretamente, o próximo passo será acessar as rotas da nossacorretamente, o próximo passo será acessar as rotas da nossa
NewsControllerNewsController ..
Mas antes, nós precisamos inserir alguns registros no nosso bancoMas antes, nós precisamos inserir alguns registros no nosso banco
de dados, para que possamos retornar às nossas pesquisas. Abra ode dados, para que possamos retornar às nossas pesquisas. Abra o
seuseu Robo 3TRobo 3T , acesse o seu servidor, acesse o seu servidor localhostlocalhost e vá até a sua base dee vá até a sua base de
dadosdados db_portaldb_portal . Clique em. Clique em CollectionsCollections, clique com o botão direito, clique com o botão direito
do seu mouse emdo seu mouse em newsnews e em e em Insert Document Insert Document . Em seguida, cole o. Em seguida, cole o
seguinte trecho de código na modal e seguinte trecho de código na modal e clique emclique em savesave..
{{
"chapeu""chapeu" : : "vitória no MasterChef""vitória no MasterChef",,
"titulo""titulo" : : "'O grito saiu da garganta', conta Danielle após rever"'O grito saiu da garganta', conta Danielle após rever
vitória no MasterChef"vitória no MasterChef",,
"texto""texto" : : "Vencedora do sétimo episódio do MasterChef Brasil 2020, a"Vencedora do sétimo episódio do MasterChef Brasil 2020, a
economista Danielle comentou os melhores momentos de economista Danielle comentou os melhores momentos de sua passagem pelasua passagem pela
cozinha mais famosa do país. A campeã disse que cozinha mais famosa do país. A campeã disse que a vitória coroou suaa vitória coroou sua
dedicação à Gastronomia e que vencer o programa mudou a dedicação à Gastronomia e que vencer o programa mudou a sua vidasua vida
profissional. “O grito saiu da garganta”, contou após rever a decisão dosprofissional. “O grito saiu da garganta”, contou após rever a decisão dos
jurados."jurados.",,
"autor""autor" : : "Da Redação""Da Redação",,
"imagem""imagem" : : "https://imagem"https://imagem.band.com.br/n.band.com.br/novahome/451a72ovahome/451a72ca-e766-4422-ca-e766-4422-
81c2-fa2f0a09e2d2.jpg"81c2-fa2f0a09e2d2.jpg",,
"link""link" : :
"https://entr"https://entretenimento.bandetenimento.band.uol.com.br/ma.uol.com.br/masterchef/noticsterchef/noticias/16308993/--ias/16308993/--o-o-
grito-saiu-dagrito-saiu-da-garganta---con-garganta---conta-danielle-apta-danielle-apos-rever-vitoros-rever-vitoria-no-mastercheia-no-masterchef"f",,
"dataPublicacao""dataPublicacao" : : "2020-08-28T14:50:43.653""2020-08-28T14:50:43.653",,
"tags""tags":: "mc2020""mc2020",,
"ativo""ativo" : true : true
}}
{{
"chapeu""chapeu" : : "Masterchef 2020""Masterchef 2020",,
"titulo""titulo" : : "10 fotos que provam que Erick "10 fotos que provam que Erick Jacquin é muito estiloso"Jacquin é muito estiloso",,
"texto""texto" : : "No episódio dessa terça-feira, 25, o jurado Erick Jacquin"No episódio dessa terça-feira, 25, o jurado Erick Jacquin
surpreendeu a todos ao participar do MasterChef Brasil com muita pompa esurpreendeu a todos ao participar do MasterChef Brasil com muita pompa e
estilo O chef de cozinha apostou em umestilo O chef de cozinha apostou em um terno azul ciano e pantufas! Oterno azul ciano e pantufas! O
estilo. O chef de cozinha apostou em um estilo. O chef de cozinha apostou em um terno azul ciano e pantufas! Oterno azul ciano e pantufas! O
detalhe não passou desapercebido por Ana Paula Padrão, mas essa não é detalhe não passou desapercebido por Ana Paula Padrão, mas essa não é aa
primeira vez que o francês chama atenção por seus primeira vez que o francês chama atenção por seus looks. Por isso, olooks. Por isso, o
Portal da Band separou 10 fotos que Portal da Band separou 10 fotos que provam que Jacquin é um dos provam que Jacquin é um dos caras maiscaras mais
estilosos que nós conhecemos."estilosos que nós conhecemos.",,
"autor""autor" : : "Da Redação""Da Redação",,
"imagem""imagem" : : "https://imagem"https://imagem.band.com.br/n.band.com.br/novahome/00ee18ovahome/00ee18f0-270c-4e3d-f0-270c-4e3d-
94d1-62250745f449.jpg"94d1-62250745f449.jpg",,
"link""link" : :
"https://entr"https://entretenimento.bandetenimento.band.uol.com.br/ma.uol.com.br/masterchef/noticsterchef/noticias/16308890/10ias/16308890/10--
fotos-que-profotos-que-provam-que-erick-jvam-que-erick-jacquin-e-muitoacquin-e-muito-estiloso"-estiloso",,
"dataPublicacao""dataPublicacao" : : "2020-08-30T14:50:43.653""2020-08-30T14:50:43.653",,
"tags""tags":: "mc2020""mc2020",,
"ativo""ativo" : true : true
}}
Agora que nós p Agora que nós populamos a nossa bopulamos a nossa base de dados, vamos faase de dados, vamos fazer zer
algumas requisições nas rotas da nossa classealgumas requisições nas rotas da nossa classe NewsControllerNewsController ..VVamos começar com amos começar com a listagem completa. Para isso, abra a listagem completa. Para isso, abra oo
seguinte endereço no seu seguinte endereço no seu navegador:navegador:
http://localhost:5000/api/v1/news/1/2http://localhost:5000/api/v1/news/1/2
ResultadoResultado
{{
"result""result": {: {
"Page""Page": 1,: 1,
"Qtd""Qtd": 10,: 10,
"Total""Total": 2,: 2,
"Data""Data": [: [
{{
"_id""_id":: "5f84e9679fbb97678fa00c8f""5f84e9679fbb97678fa00c8f",,
"chapeu""chapeu":: "vitória no MasterChef""vitória no MasterChef",,
"titulo""titulo":: "'O grito saiu da garganta', conta Danielle após rever vitória"'O grito saiu da garganta', conta Danielle após rever vitória
no MasterChef"no MasterChef",,
"texto""texto":: "Vencedora do sétimo episódio do MasterChef Brasil 2020, a"Vencedora do sétimo episódio do MasterChef Brasil 2020, a
economistaDanielle comentou os melhores momentos de economista Danielle comentou os melhores momentos de sua passagem pelasua passagem pela
cozinha mais famosa do país. A campeã disse que cozinha mais famosa do país. A campeã disse que a vitória coroou suaa vitória coroou sua
dedicação à Gastronomia e que vencer o programa mudou a dedicação à Gastronomia e que vencer o programa mudou a sua vidasua vida
profissional. “O grito saiu da garganta”, contou após rever a decisão dosprofissional. “O grito saiu da garganta”, contou após rever a decisão dos
jurados."jurados.",,
"autor""autor":: "Da Redação""Da Redação",,
"imagem""imagem":: "https://imag"https://imagem.band.com.brem.band.com.br/novahome/451a/novahome/451a72ca-e766-4422-72ca-e766-4422-81c2-81c2-
fa2f0a09e2d2.jpg"fa2f0a09e2d2.jpg",,
"link""link"::
"https://entr"https://entretenimento.bandetenimento.band.uol.com.br/ma.uol.com.br/masterchef/noticsterchef/noticias/16308993/--ias/16308993/--o-o-
grito-saiu-dagrito-saiu-da-garganta---con-garganta---conta-danielle-apta-danielle-apos-rever-vitoros-rever-vitoria-no-mastercheia-no-masterchef"f",,
"dataPublicacao""dataPublicacao":: "2020-08-28T17:50:43.653Z""2020-08-28T17:50:43.653Z",,
"tags""tags":: "mc2020""mc2020",,
"ativo""ativo": true: true
},},
{{
"_id""_id":: "5f84e9679fbb97678fa00c92""5f84e9679fbb97678fa00c92",,
"chapeu""chapeu":: "Masterchef 2020""Masterchef 2020",,
"titulo""titulo":: "10 fotos que provam que Erick Jacquin é "10 fotos que provam que Erick Jacquin é muito estiloso"muito estiloso",,
"texto""texto":: "No episódio dessa terça-feira, 25, o jurado Erick Jacquin"No episódio dessa terça-feira, 25, o jurado Erick Jacquin
surpreendeu a todos ao participar do MasterChef Brasil com muita pompa esurpreendeu a todos ao participar do MasterChef Brasil com muita pompa e
estilo. O chef de cozinha apostou em um estilo. O chef de cozinha apostou em um terno azul ciano e pantufas! Oterno azul ciano e pantufas! O
detalhe não passou desapercebido por Ana Paula Padrão, mas essa não é detalhe não passou desapercebido por Ana Paula Padrão, mas essa não é aa
primeira vez que o francês chama atenção por seus primeira vez que o francês chama atenção por seus looks. Por isso, olooks. Por isso, o
Portal da Band separou 10 fotos que Portal da Band separou 10 fotos que provam que Jacquin é um dos provam que Jacquin é um dos caras maiscaras mais
estilosos que nós conhecemos."estilosos que nós conhecemos.",,
"autor""autor":: "Da Redação""Da Redação",,
"imagem""imagem":: "https://imag"https://imagem.band.com.brem.band.com.br/novahome/00ee/novahome/00ee18f0-270c-4e3d-18f0-270c-4e3d-94d1-94d1-
62250745f449.jpg"62250745f449.jpg",,
"link""link"::
"https://entr"https://entretenimento.bandetenimento.band.uol.com.br/ma.uol.com.br/masterchef/noticsterchef/noticias/16308890/10ias/16308890/10--
fotos-que-profotos-que-provam-que-erick-jvam-que-erick-jacquin-e-muitoacquin-e-muito-estiloso"-estiloso",,
"dataPublicacao""dataPublicacao":: "2020-08-30T17:50:43.653Z""2020-08-30T17:50:43.653Z",,
"tags""tags":: "mc2020""mc2020",,
"ativo""ativo": true: true
}}
]]
}}
}}
Note que nesse resultado nós temos:Note que nesse resultado nós temos:
Page Page : a página que passamos como parâmetro:: a página que passamos como parâmetro: /api/news/1/api/news/1 ..
Qtd Qtd
: quantidade que estamos solicitando via parâmetro:: quantidade que estamos solicitando via parâmetro: /api/news/1/1/api/news/1/100 ..
Total Total : total de registros que nós temos no nosso banco de: total de registros que nós temos no nosso banco de
dados.dados.
Data Data : resultado da nossa busca. Note que retornaram os dois: resultado da nossa busca. Note que retornaram os dois
registros adicionados no passo anterior através doregistros adicionados no passo anterior através do Robo 3TRobo 3T ..
Agora abra o e Agora abra o endereço:ndereço: http://localhost:3050/api/v1/news/http://localhost:3050/api/v1/news/ seguido seguido
de um dosde um dos _id_id ..
ResultadoResultado
{{
"result""result": {: {
"_id""_id":: "5f84e9679fbb97678fa00c8f""5f84e9679fbb97678fa00c8f",,
"chapeu""chapeu":: "vitória no MasterChef""vitória no MasterChef",,
"titulo""titulo":: "'O grito saiu da garganta', conta Danielle após rever vitória"'O grito saiu da garganta', conta Danielle após rever vitória
no MasterChef"no MasterChef",,
"texto""texto":: "Vencedora do sétimo episódio do MasterChef Brasil 2020, a"Vencedora do sétimo episódio do MasterChef Brasil 2020, a
economista Danielle comentou os melhores momentos de economista Danielle comentou os melhores momentos de sua passagem pelasua passagem pela
cozinha mais famosa do país. A campeã disse que cozinha mais famosa do país. A campeã disse que a vitória coroou suaa vitória coroou sua
dedicação à Gastronomia e que vencer o programa mudou a dedicação à Gastronomia e que vencer o programa mudou a sua vidasua vida
profissional. “O grito saiu da garganta”, contou após rever a decisão dosprofissional. “O grito saiu da garganta”, contou após rever a decisão dos
jurados."jurados.",,
"autor""autor":: "Da Redação""Da Redação",,
"imagem""imagem":: "https://imag"https://imagem.band.com.brem.band.com.br/novahome/451a/novahome/451a72ca-e766-4422-72ca-e766-4422-81c2-81c2-
fa2f0a09e2d2.jpg"fa2f0a09e2d2.jpg",,
"link""link"::
"https://entr"https://entretenimento.bandetenimento.band.uol.com.br/ma.uol.com.br/masterchef/noticsterchef/noticias/16308993/--ias/16308993/--o-o-
grito-saiu-dagrito-saiu-da-garganta---con-garganta---conta-danielle-apta-danielle-apos-rever-vitoros-rever-vitoria-no-mastercheia-no-masterchef"f",,
"dataPublicacao""dataPublicacao":: "2020-08-28T17:50:43.653Z""2020-08-28T17:50:43.653Z",,
"tags""tags":: "mc2020""mc2020",,
"ativo""ativo": true: true
}}
}}
Caso queria buscar uma outra Caso queria buscar uma outra notícia, basta trocar onotícia, basta trocar o _id_id na suana sua
requisição.requisição.
Antes de finali Antes de finalizar este capítulo, vazar este capítulo, vamos adicionar um novo mos adicionar um novo pacote aopacote ao
nosso projeto chamadonosso projeto chamado nodemonnodemon . Para quem não conhece esse. Para quem não conhece esse
pacote, opacote, o nodemonnodemon reinicia o servidor automaticamente sempre quereinicia o servidor automaticamente sempre que
p ,p , p qp q
você salva um arquivo que o servidor está utilizando. O que issovocê salva um arquivo que o servidor está utilizando. O que isso
significa?significa?
Significa que nós não precisamos dar Significa que nós não precisamos dar stop/start na nossa aplicaçãostop/start na nossa aplicação
toda vez que fizermos uma alteração no nosso projeto, pois eletoda vez que fizermos uma alteração no nosso projeto, pois ele
verifica que ocorreu uma alteração e já faz overifica que ocorreu uma alteração e já faz o refreshrefresh
automaticamente. Para importá-lo digite o seguinte comando no seuautomaticamente. Para importá-lo digite o seguinte comando no seu
terminal:terminal:
npm npm install install nodemon nodemon --save-dev--save-dev
Com oCom o nodemonnodemon instalado, atualize o seguinte trecho de código noinstalado, atualize o seguinte trecho de código no
seu arquivoseu arquivo package.jsonpackage.json ::
"scripts""scripts": {: {
//outras //outras implementaçõeimplementaçõess
"start""start":: "nodemon "nodemon ./dist/progra./dist/program.js"m.js"
Com esse ajuste, toda vez que você alterar algum arquivo do seuCom esse ajuste, toda vez que você alterar algum arquivo do seu
projeto, ele vai reconhecer e darprojeto, ele vai reconhecer e dar refreshrefresh na sua aplicação. na sua aplicação.
Antes de finali Antes de finalizar este capítulo, qzar este capítulo, que tal um resumo due tal um resumo de tudo o quee tudo o que
aprendemos?aprendemos?
Iniciando pela parte de modelagem de software, de maneira bemIniciando pela parte de modelagem de software, de maneira bem
simples, nós modelamos uma API que retorna as notícias de umsimples, nós modelamos uma API que retorna as notícias de um
programa. Vprograma.Voltamos aos capítulos 4 e oltamos aos capítulos 4 e 5 deste livro, para 5 deste livro, para reforçar osreforçar os
conceitos de POO (Programação Orientada a Objetos) conceitos de POO (Programação Orientada a Objetos) e interfaces.e interfaces.
Aprendemos o qu Aprendemos o que sãoe são promisespromises , vimos como trabalhar com, vimos como trabalhar com
try/catchtry/catch para tratamento de exceções e, além de desenvolver apara tratamento de exceções e, além de desenvolver a
nossa API, nós aprendemos algumas boas práticas no momento denossa API, nós aprendemos algumas boas práticas no momento de
desenhar a arquitetura do nosso projeto.desenhar a arquitetura do nosso projeto.
Com isso, nós finalizamos mais este capítulo. No próximo, daremosCom isso, nós finalizamos mais este capítulo. No próximo, daremos
continuidade ao desenvolvimento da nossa API criando novas rotas.continuidade ao desenvolvimento da nossa API criando novas rotas.
C 12C 12
Criando novas modelsCriando novas models
Com a estrutura básica do nosso projeto OK, vamos avançar neleCom a estrutura básica do nosso projeto OK, vamos avançar nele
desenvolvendo duas novas rotas. Para isso, desenvolvendo duas novas rotas. Para isso, imagine o seguinteimagine o seguinte
cenário: chegou uma nova demanda para a cenário: chegou uma nova demanda para a qual precisamosqual precisamos
desenvolver duas novas rotas, uma para listagem de vídeos e umadesenvolver duas novas rotas, uma para listagem de vídeos e uma
outra para listagem de galeria de fotos.outra para listagem de galeria de fotos.
Voltando ao capítulo anterior, o primeiro passo para a criação dasVoltando ao capítulo anterior, o primeiro passo para a criação das
nossas rotas será a definição das nossas models. Para isso, nósnossas rotas será a definição das nossas models. Para isso, nós
precisamos responder às precisamos responder às seguintes perguntas:seguintes perguntas:
O que todo vídeo tem?O que todo vídeo tem?
O que toda galeria de fotos tem?O que toda galeria de fotos tem?
Em uma breve análise, nós temos:Em uma breve análise, nós temos:
Model de vídeos:Model de vídeos: título, texto, imagem, título, texto, imagem, duraçãoduração, link,, link, url url , data de, data de
publicação, tags e um atributo para dizer se o vídeo está ativo oupublicação, tags e um atributo para dizer se o vídeo está ativo ou
não.não.
Model de galeria de fotos:Model de galeria de fotos: título, texto, data de título, texto, data de publicação, tags,publicação, tags,
uma imagem de destaque da galeria, link uma imagem de destaque da galeria, link da galeria de fotos,da galeria de fotos, umauma
listagem de fotoslistagem de fotos e um atributo para dizer se ela está e um atributo para dizer se ela está ativa ou não.ativa ou não.
Caso você não tenha percebido, as models acima têm algunsCaso você não tenha percebido, as models acima têm alguns
atributos em comum com a nossaatributos em comum com a nossa NewsSchemaNewsSchema ::
exportexport constconst NewsSchema = NewsSchema = newnew mongoose.Schemmongoose.Schema({a({
titulo: { type:titulo: { type: StringString }, },
chapeu: { type:chapeu: { type: StringString }, },
texto: { type:texto: { type: StringString }, },
autor: { type:autor: { type: StringString }, },
imagem: { type:imagem: { type: StringString }, },
dataPublicacaodataPublicacao: { : { type:type: DateDate }, },
tags: { type:tags: { type: StringString }, },
link: { type: link: { type: StringString }, },
ativo: { type:ativo: { type: BooleanBoolean } }
});});
Em uma breve análise, podemos observar que, entre as models deEm uma breve análise, podemos observar que, entre as models de
notícias e o levantamento que fizemos para as models de vídeos enotícias e o levantamento que fizemos para as models de vídeos e
galeria de fotos, temos as galeria de fotos, temos as seguintes propriedades em comum:seguintes propriedades em comum: títulotítulo,,
textotexto,, imagemimagem,, data de publicaçãodata de publicação,, tagstags,, link link e e ativoativo..
Tendo isso em mente e pensando no paradigma de Orientação aTendo isso em mente e pensando no paradigma de Orientação a
Objetos, qual seria a melhor forma de criarmos essas novas modelsObjetos, qual seria a melhor forma de criarmos essas novas models
aproveitando as propriedades que elas têm em comum?aproveitando as propriedades que elas têm em comum?
Caso a sua resposta seja criando uma classeCaso a sua resposta seja criando uma classe abstrataabstrata e utilizando o e utilizando o
conceito deconceito de herançaherança, você está certa(o). Mas, para que possamos, você está certa(o). Mas, para que possamos
criar uma classe modelo para que todas as outras herdem dela, nóscriar uma classe modelo para que todas as outras herdem dela, nósprecisaremos fazer algumas modificações no nosso código.precisaremos fazer algumas modificações no nosso código.
Caso não se recorde do que Caso não se recorde do que é uma classe abstrata, eu recomendoé uma classe abstrata, eu recomendo
que você volte ao capítulo 4 deste livro, sobre Orientação a Objetos.que você volte ao capítulo 4 deste livro, sobre Orientação a Objetos.
12.1 POO (Programação Orientada a Objetos) na12.1 POO (Programação Orientada a Objetos) na
práticaprática
Nosso primeiro passo será a criação da classe modelo, a nossaNosso primeiro passo será a criação da classe modelo, a nossa
classeclasse abstrataabstrata. Crie um novo arquivo chamado. Crie um novo arquivo chamado core.tscore.ts dentro dodentro do
diretóriodiretório modelsmodels e atualize-o com o e atualize-o com o seguinte trecho de código:seguinte trecho de código:
exportexport abstract abstract classclass Core { Core {
titulo:titulo: StringString;;
texto:texto: StringString;;
imagem:imagem: StringString;;
dataPublicacao:dataPublicacao: DateDate;;
tags:tags: StringString;;
link: link: StringString;;
ativo:ativo: BooleanBoolean;;
}}
Note que a classeNote que a classe CoreCore tem todas as propriedades em comum entretem todas as propriedades em comum entre
as nossas models.as nossas models.
O nosso próximo passo será a atualização do arquivoO nosso próximo passo será a atualização do arquivo newsSchema.tsnewsSchema.ts ..
Precisamos mover a configuração com o pacotePrecisamos mover a configuração com o pacote mongoosemongoose para opara o
arquivoarquivo newsRepository.tsnewsRepository.ts e criar uma nova classe para a e criar uma nova classe para a model demodel de
notícias, para que ela possa herdar as notícias, para que ela possa herdar as propriedades depropriedades de Core.tsCore.ts ..
Renomeie o arquivoRenomeie o arquivo newsSchema.tsnewsSchema.ts parapara news.tsnews.ts , em seguida, em seguida
atualize-o com o seguinte trecho de atualize-o com o seguinte trecho de código:código:
importimport { Core } from { Core } from "./core""./core";;
exportexport classclass News extends Core { News extends Core {
chapeu:chapeu: StringString;;
autor:autor: StringString;;
}}
Agora atualize Agora atualize o arquivoo arquivo newsRepository.tsnewsRepository.ts com o seguinte trecho decom o seguinte trecho de
código:código:
importimport mongoose from mongoose from "mongoose""mongoose";;
importimport { News } from { News } from "../models/news""../models/news";;
constconst NewsSchema = NewsSchema = newnew mongoose.Schemmongoose.Schema<News>({a<News>({
titulo: { type:titulo: { type: StringString }, },
chapeu: { type:chapeu: { type: StringString }, },
texto: { type:texto: { type: StringString }, },
autor: { type:autor: { type: StringString }, },
imagem: { type:imagem: { type: StringString }, },
dataPublicacaodataPublicacao: { : { type:type: DateDate }, },
tags: { type:tags: { type: StringString }, },
link: { type:link: { type: StringString }, },
ativo: { type:ativo: { type: BooleanBoolean } }
});});
tt tt N R itN R it d ld l<N >(<N >(" "" " N S h )N S h )
exportexport constconst NewsRepository = NewsRepository = mongoose.modelmongoose.model<News>(<News>("news""news",NewsSchema);, NewsSchema);
Até aqui, nós cri Até aqui, nós criamos uma nova classe chamaamos uma nova classe chamadada NewsNews , que está, que está
herdando todas as propriedades da nossa classeherdando todas as propriedades da nossa classe CoreCore e, eme, em
seguida, passamos o valor deseguida, passamos o valor de NewsSchemaNewsSchema para o arquivopara o arquivo
newsRepository.tsnewsRepository.ts ..
Caso você esteja com o seu compilador executando, no console doCaso você esteja com o seu compilador executando, no console do
Visual Studio Code está aparecendo o seguinte erro:Visual Studio Code está aparecendo o seguinte erro:
/* Erro na console*//* Erro na console*/
TypeType 'News''News' does not satisfy the constraint does not satisfy the constraint 'Document<any>''Document<any>'..
TypeType 'News''News' is missing the is missing the following properties from typefollowing properties from type
'Document<any>''Document<any>': $ignore, $isDefault, $isDeleted, $isEmpty, and 45 : $ignore, $isDefault, $isDeleted, $isEmpty, and 45 more.more.
Esse erro diz que a model que estamos passando para o nossoEsse erro diz que a model que estamos passando para o nosso
repositório não está estendendo derepositório não está estendendo de DocumentDocument do pacotedo pacote mongoosemongoose ..
Para resolver isso, basta estenderPara resolver isso, basta estender DocumentDocument na classena classe CoreCore ::
importimport { Document } from { Document } from 'mongoose''mongoose';;
exportexport abstract abstract classclass Core extends Document { Core extends Document {
/* atributos da classe news*//* atributos da classe news*/
}}
Com a criação da classe abstrataCom a criação da classe abstrata CoreCore e a atualização da classe dee a atualização da classe de
notícias, vamos agora criar as nossas models de vídeos e galeria denotícias, vamos agora criar as nossas models de vídeos e galeria de
fotos.fotos.
Crie um arquivo chamadoCrie um arquivo chamado videos.tsvideos.ts , um chamado, um chamado galeria.tsgaleria.ts e ume um
outro chamadooutro chamado fotos.tsfotos.ts dentro do diretóriodentro do diretório modelsmodels . Em seguida,. Em seguida,
atualize-os com os seguintes trechos de atualize-os com os seguintes trechos de código:código:
/* videos.ts*//* videos.ts*/
importimport { Core } from { Core } from "./core""./core";;
exportexport classclass Videos extends Core { Videos extends Core {
url:url: StringString;;
duracao:duracao: StringString;;
}}
/*galeria.ts*//*galeria.ts*/
importimport { Core } from { Core } from "./core""./core";;
importimport { Fotos } from { Fotos } from "./fotos""./fotos";;
exportexport classclass Galeria extends Core { Galeria extends Core {
fotos:fotos: ArrayArray<Fotos>;<Fotos>;
}}
/*fotos.ts*//*fotos.ts*/
exportexport classclass Fotos Fotos {{
thumb:thumb: StringString;;
thumbNail:thumbNail: StringString;;
credito:credito: StringString;;
legenda:legenda: StringString;;
}}
Com esses arquivos criados, nós finalizamos a parte de criação dasCom esses arquivos criados, nós finalizamos a parte de criação das
nossas models. O próximo passo será a criação dos nossas models. O próximo passo será a criação dos nossosnossos
repositórios.repositórios.
Criando os novos repositóriosCriando os novos repositórios
Para a parte de Para a parte de criação dos repositórios, nós somente precisamoscriação dos repositórios, nós somente precisamos
seguir o modelo atualizado emseguir o modelo atualizado em newsRepository.tsnewsRepository.ts ..
Crie dois novos arquivos dentro do Crie dois novos arquivos dentro do diretóriodiretório repositoryrepository , um, um
chamadochamado videosRepository.tsvideosRepository.ts e um outro chamadoe um outro chamado
galeriaRepository.tsgaleriaRepository.ts , em seguida atualize-os com os trechos de, em seguida atualize-os com os trechos de
código:código:
/*videosRepository.ts*//*videosRepository.ts*/
importimport mongoose from mongoose from "mongoose""mongoose";;
importimport { Videos } from { Videos } from "../models/videos""../models/videos";;
constconst VideosSchema = VideosSchema = newnew mongoose.Schemamongoose.Schema<Videos>({<Videos>({
titulo: {titulo: { StringString }, },
texto: {texto: { StringString }, },
imagem: {imagem: { StringString }, },
duracao: {duracao: { StringString }, },
link: link: {{ StringString }, },
url: {url: { StringString }, },
dataPublicacaodataPublicacao: : {{ DateDate }, },
tags: {tags: { StringString }, },
ativo: {ativo: { BooleanBoolean } }
});});
exportexport constconst VideosRepository = VideosRepository = mongoose.modelmongoose.model<Videos>(<Videos>("videos""videos",,
VideosSchema);VideosSchema);
/*galeriaRepository.ts*//*galeriaRepository.ts*/
importimport mongoose from mongoose from "mongoose""mongoose";;
importimport { Fotos } from { Fotos } from "../models/fotos""../models/fotos";;
importimport { Galeria } from { Galeria } from "../models/galeria""../models/galeria";;
constconst GaleriaSchema = GaleriaSchema = newnew mongoose.Schemongoose.Schema<Galeria>({ma<Galeria>({
titulo: {titulo: { StringString }, },
texto: {texto: { StringString }, },
dataPublicacaodataPublicacao: : {{ DateDate }, },
fotos: [fotos: [ArrayArray<Fotos>()],<Fotos>()],
ativo: {ativo: { BooleanBoolean } }
});});
exportexport constconst GaleriaRepositGaleriaRepository ory = = mongoose.modemongoose.model<Galeria>(l<Galeria>("galeria""galeria",,
GaleriaSchema);GaleriaSchema);
Com os repositórios criados, criaremos agora os nossos contratos,Com os repositórios criados, criaremos agora os nossos contratos,
services e as controllers das nossas novas models.services e as controllers das nossas novas models.
12.2 Generics e tipagem de retorno de funções12.2 Generics e tipagem de retorno de funções
na práticana prática
Iniciando pelos contratos, nós precisaremos criar duas novasIniciando pelos contratos, nós precisaremos criar duas novas
interfaces, uma para vídeos e outra para galeria de fotos. E cadainterfaces, uma para vídeos e outra para galeria de fotos. E cada
uma delas deve conter dois métodos, um para buscar umuma delas deve conter dois métodos, um para buscar um
determinado resultado pelo seudeterminado resultado pelo seu idid e outro para retornar todos ose outro para retornar todos os
dados de uma collection da nossa dados de uma collection da nossa base de dados, passando doisbase de dados, passando dois
parâmetros para paginação.parâmetros para paginação.
Analisando a i Analisando a interfacenterface INewsServiceINewsService , que nós criamos no capítulo, que nós criamos no capítulo
anterior, note que nós já temos um contrato que retorna exatamenteanterior, note que nós já temos um contrato que retorna exatamente
o que nós precisamos:o que nós precisamos:
/*iNewsService.ts.ts*//*iNewsService.ts.ts*/
exportexport interfaceinterface INewsService { INewsService {
getget(id:(id: stringstring););
getAll(page:getAll(page: numbernumber, qtd:, qtd: numbernumber): ): Promise<ResultPromise<Result>;>;
}}
Nele nós temos:Nele nós temos:
Um método que retorna os dados de umaUm método que retorna os dados de uma entidadeentidade pelo seu pelo seu
idid ;;
E um outro método que retorna umaE um outro método que retorna uma promisepromise da classeda classe ResultResult ..
exportexport classclass Result { Result {
Qtd:Qtd: numbernumber;;
Page:Page: numbernumber;;
Total:Total: numbernumber;;
Data:Data: anyany
}}
Uma das formas de resolver a nossa demanda seria duplicar aUma das formas de resolver a nossa demanda seria duplicar a
nossa interfacenossa interface INewsServiceINewsService , renomeando-a para, renomeando-a para IVideosServiceIVideosService ee
IGaleriaServiceIGaleriaService , mas como nós já , mas como nós já aprendemos neste livro, aaprendemos neste livro, a
duplicação de código pode gerar problemas futuros.duplicação de código pode gerar problemas futuros.
Então como podemos manter o nosso código organizadoe criar osEntão como podemos manter o nosso código organizado e criar os
novos contratos sem duplicar a novos contratos sem duplicar a interfaceinterface INewsServiceINewsService ??
Se você respondeu "utilizandoSe você respondeu "utilizando herançaherança", a sua resposta está 50%", a sua resposta está 50%
correta. Para ela ficar 100% correta, vamos voltar ao capítulo 2,correta. Para ela ficar 100% correta, vamos voltar ao capítulo 2,
sobre tipagem. O nosso código sobre tipagem. O nosso código não ficaria mais organizado senão ficaria mais organizado se
soubéssemos o retorno do métodosoubéssemos o retorno do método getget e da e da propriedadepropriedade DataData dada
classeclasse ResultResult ??
Acredito que a Acredito que a sua resposta tenha sua resposta tenha sido "sido "sim"sim", pois, dessa forma,, pois, dessa forma,
além de o código ficar além de o código ficar organizado, nós conseguimos utilizar um dosorganizado, nós conseguimos utilizar um dos
maiores recursos para se maiores recursos para se trabalhar com Typtrabalhar com TypeScript, a tipagem.eScript, a tipagem.
Crie um novo arquivo dentro do Crie um novo arquivo dentro do diretóriodiretório contractscontracts chamadochamado
iService.tsiService.ts , com o seguinte trecho de código:, com o seguinte trecho de código:
/*iService.ts*//*iService.ts*/
importimport { Result } from { Result } from "../infra/result""../infra/result";;
exportexport interfaceinterface IService<T> { IService<T> {
getget(id:(id: stringstring): Promise<T>;): Promise<T>;
getAll(page:getAll(page: numbernumber, qtd:, qtd: numbernumber): ): Promise<ResultPromise<Result<T>>;<T>>;
}}
Analisando o co Analisando o contratontrato IService<T>IService<T> , nós criamos uma interface que, nós criamos uma interface que
recebe um parâmetro genérico que está sendo recebe um parâmetro genérico que está sendo passado para ospassado para os
métodosmétodos getAllgetAll ee getget , que copiamos da , que copiamos da interfaceinterface INewsServiceINewsService ..
VVocê se lembra dos ocê se lembra dos Generics, que nós aprendemos no capítulo 6Generics, que nós aprendemos no capítulo 6
deste livro? Essa é uma das formas como podemos trabalhar comdeste livro? Essa é uma das formas como podemos trabalhar com
eles no nosso dia a dia.eles no nosso dia a dia.
Caso você esteja com o seu compilador executando, no console doCaso você esteja com o seu compilador executando, no console do
Visual Studio Code está aparecendo o seguinte erro:Visual Studio Code está aparecendo o seguinte erro:
/* Erro na console*//* Erro na console*/
error TS2315: Typeerror TS2315: Type 'Result''Result' is not is not generic.generic.
getAll(page:getAll(page: numbernumber, qtd:, qtd: numbernumber): ): Promise<ResultPromise<Result<T>>;<T>>;
Esse erro está informando que a classeEsse erro está informando que a classe ResultResult não é genérica. Paranão é genérica. Para
resolvê-lo atualize o seu arquivoresolvê-lo atualize o seu arquivo result.tsresult.ts , com o seguinte trecho, com o seguinte trecho
de código:de código:
/*result.ts*//*result.ts*/
exportexport classclass Result<T> { Result<T> {
Qtd:Qtd: numbernumber;;
Page:Page: numbernumber;;
Total:Total: numbernumber;;
Data:Data: ArrayArray<T><T>
}}
Com a classeCom a classe Result<T>Result<T> OK, vamos atualizar a interfaceOK, vamos atualizar a interface
INewsServiceINewsService para que ela passe a herdar depara que ela passe a herdar de IService<T>IService<T> e criar ose criar os
dois novos contratosdois novos contratos iVideosService.tsiVideosService.ts ee iGaleriaService.tsiGaleriaService.ts seguindoseguindo
o exemplo deo exemplo de INewsServiceINewsService ..
/*iNewsService.ts*//*iNewsService.ts*/
importimport { News } from { News } from "../models/news""../models/news";;
importimport { IService } from { IService } from "./iService""./iService";;
exportexport interfaceinterface INewsService extends IService<News> { } INewsService extends IService<News> { }
/*iVideosService.ts*//*iVideosService.ts*/
importimport { Videos } from { Videos } from "../models/videos""../models/videos";;
importimport { IService } from { IService } from "./iService""./iService";;
exportexport interfaceinterface IVideosService extends IService<Videos> { } IVideosService extends IService<Videos> { }
/*iGaleriaService.ts*//*iGaleriaService.ts*/
importimport { Galeria } from { Galeria } from "../models/galeria""../models/galeria";;
importimport { IService } from { IService } from "./iService""./iService";;
exportexport interfaceinterface IGaleriaService extends IService<Galeria> { } IGaleriaService extends IService<Galeria> { }
Com a organização e a criação dos nossos contratos, o próximoCom a organização e a criação dos nossos contratos, o próximo
passo será a atualização do passo será a atualização do arquivoarquivo newsService.tsnewsService.ts e a criação dase a criação das
classes que devem implementar os novos contratos de vídeo eclasses que devem implementar os novos contratos de vídeo e
galeria de fotos.galeria de fotos.
Atualizando os serviçosAtualizando os serviços
Iniciando pela classeIniciando pela classe NewsServiceNewsService , atualize-a com o , atualize-a com o seguinte trechoseguinte trechode código:de código:
gg
/*newsService.ts*//*newsService.ts*/
//outras implementações//outras implementações
importimport { News } from { News } from "../models/news""../models/news";;
exportexport classclass NewsService NewsService implementsimplements INewsService { INewsService {
asyncasync getget(_id:(_id: stringstring): Promise<News> {): Promise<News> {
letlet result result = await = await NewsRepository.NewsRepository.findById(_id);findById(_id);
returnreturn result; result;
}}
async getAll(page:async getAll(page: numbernumber, qtd:, qtd: numbernumber): Promise<Result<News>> {): Promise<Result<News>> {
letlet result = result = newnew Result<News>()Result<News>();;
result.Page = page;result.Page = page;
result.Qtd = qtd;result.Qtd = qtd;
result.Total = result.Total = await NewsRepository.count({});await NewsRepository.count({});
result.Data = await result.Data = await NewsRepositoryNewsRepository.find({}).skip.find({}).skip((page * ((page * qtd) -qtd) -
qtd).limit(qtd);qtd).limit(qtd);
returnreturn result; result;
}}
}}
Analisando as a Analisando as alterações realizadalterações realizadas no arquivos no arquivo newsService.tsnewsService.ts , nós, nós
temos:temos:
Alteramos o método Alteramos o método getget para que ele retorne umapara que ele retorne uma PromisePromise dada
modelmodel NewsNews ..
EmEm getAllgetAll , passamos a model, passamos a model NewsNews para a classepara a classe ResultResult , que, que
deve retornar umadeve retornar uma PromisePromise dede NewsNews na propriedadena propriedade DataData ..
Com a classeCom a classe newsService.tsnewsService.ts atualizada, crie dois novos arquivosatualizada, crie dois novos arquivos
dentro do diretóriodentro do diretório servicesservices , um chamado, um chamado videosService.tsvideosService.ts e ume um
outro chamadooutro chamado galeriaService.tsgaleriaService.ts e, em seguida, atualize-os com ose, em seguida, atualize-os com os
seguintes trechos de código:seguintes trechos de código:
/*videosService.ts*//*videosService.ts*/
importimport { IVideosService } from { IVideosService } from "../contracts/iVideosService""../contracts/iVideosService";;
importimport { Result } from { Result } from "../infra/result""../infra/result";;
importimport { Videos } from{ Videos } from " /models/videos"" /models/videos";;
importimport { Videos } from { Videos } from ../models/videos../models/videos ;;
importimport { VideosRepository } from { VideosRepository } from "../repository/videosRepository""../repository/videosRepository";;
exportexport classclass VideosService VideosService implementsimplements IVideosService { IVideosService {
asyncasync getget(_id:(_id: stringstring): Promise<Videos> {): Promise<Videos>{
letlet result result = await = await VideosRepositorVideosRepository.findById(_idy.findById(_id););
returnreturn result; result;
}}
async getAll(page:async getAll(page: numbernumber, qtd:, qtd: numbernumber): Promise<Result<Videos>> {): Promise<Result<Videos>> {
letlet result = result = newnew Result<Videos>Result<Videos>();();
result.Page = page;result.Page = page;
result.Qtd = qtd;result.Qtd = qtd;
result.Total = result.Total = await VideosRepository.count({}await VideosRepository.count({}););
result.Data = await result.Data = await VideosRepositoVideosRepository.find({}).skry.find({}).skip((page * ip((page * qtd) -qtd) -
qtd).limit(qtd);qtd).limit(qtd);
returnreturn result; result;
}}
}}
/*galeriaService.ts*//*galeriaService.ts*/
importimport { IGaleriaService } from { IGaleriaService } from "../contracts/iGaleriaService""../contracts/iGaleriaService";;
importimport { Result } from { Result } from "../infra/result""../infra/result";;
importimport { Galeria } from { Galeria } from "../models/galeria""../models/galeria";;
importimport { GaleriaRepository } from { GaleriaRepository } from "../repository/galeriaRepository""../repository/galeriaRepository";;
exportexport classclass GaleriaServiceGaleriaService implementsimplements IGaleriaService { IGaleriaService {
asyncasync getget(_id:(_id: stringstring): Promise<Galeria> {): Promise<Galeria> {
letlet result result = await = await GaleriaRepositoGaleriaRepository.findById(_iry.findById(_id);d);
returnreturn result; result;
}}
async getAll(page:async getAll(page: numbernumber, qtd:, qtd: numbernumber): Promise<Result<Galeria>> {): Promise<Result<Galeria>> {
letlet result = result = newnew Result<GaleriaResult<Galeria>();>();
result.Page = page;result.Page = page;
result.Qtd = qtd;result.Qtd = qtd;
result.Total = result.Total = await GaleriaRepository.count({await GaleriaRepository.count({});});
result.Data = await result.Data = await GaleriaRepositGaleriaRepository.find({}).sory.find({}).skip((page * qtd) kip((page * qtd) --
) ( )) ( )
qtd).limit(qtd);qtd).limit(qtd);
returnreturn result; result;
}}
}}
Com os serviços OK, o nosso próximo passo será a criação dasCom os serviços OK, o nosso próximo passo será a criação das
nossas controllers e a nossas controllers e a atualização do arquivoatualização do arquivo startUp.tsstartUp.ts com ascom as
novas rotas.novas rotas.
Criação dos arquivos de entradaCriação dos arquivos de entrada
Crie dois arquivos dentro do diretórioCrie dois arquivos dentro do diretório controllercontroller , um chamado, um chamado
videosController.tsvideosController.ts e um outro chamadoe um outro chamado galeriaController.tsgaleriaController.ts e, eme, em
seguida, atualize-os com os trechos de seguida, atualize-os com os trechos de código a seguir:código a seguir:
/*videosController.ts*//*videosController.ts*/
importimport { Request, Response } { Request, Response } fromfrom "express""express";;
importimport { VideosService } from { VideosService } from "../services/videosService""../services/videosService";;
classclass VideosController { VideosController {
privateprivate _service: _service: VideosServiceVideosService;;
constructorconstructor() {() {
thisthis._service =._service = newnew VideosService(VideosService(););
}}
asyncasync getget(request: Request, response: Response) {(request: Request, response: Response) {
trytry { {
constconst page = page = request.paramsrequest.params.page ?.page ?
parseIntparseInt(request.params(request.params.page) : .page) : 1;1;
constconst qtd = qtd = request.paramrequest.params.qtd ?s.qtd ? parseIntparseInt(request.params.qtd)(request.params.qtd)
: 10;: 10;
letlet result = await result = await thisthis._service.getA._service.getAll(page, ll(page, qtd);qtd);
response.statresponse.status(200).json({ result us(200).json({ result });});
}} catchcatch (error) { (error) {
response.statresponse.status(500).json({ error: us(500).json({ error: error.message ||error.message ||
error toStrinerror toString()g() });});
error.toStrinerror.toString() g() });});
}}
}}
async getById(request: Request, response: Response) {async getById(request: Request, response: Response) {
trytry { {
constconst _id _id = request.params.id;= request.params.id;
letlet result = await result = await thisthis._service.get(_id);._service.get(_id);
response.statresponse.status(200).json({ result us(200).json({ result });});
}} catchcatch (error) { (error) {
response.statresponse.status(500).json({ error: us(500).json({ error: error.message ||error.message ||
error.toStrinerror.toString() g() });});
}}
}}
}}
exportexport defaultdefault newnew VideosControlVideosController();ler();
/*galeriaController.ts*//*galeriaController.ts*/
importimport { Request, Response } { Request, Response } fromfrom "express""express";;
importimport { GaleriaService } from { GaleriaService } from "../services/galeriaService""../services/galeriaService";;
classclass GaleriaController { GaleriaController {
privateprivate _service: GaleriaService;_service: GaleriaService;
constructorconstructor() {() {thisthis._service =._service = newnew GaleriaServiceGaleriaService();();
}}
asyncasync getget(request: Request, response: Response) {(request: Request, response: Response) {
trytry { {
constconst page = page = request.paramsrequest.params.page ?.page ?
parseIntparseInt(request.params(request.params.page) : .page) : 1;1;
constconst qtd = qtd = request.paramrequest.params.qtd ?s.qtd ? parseIntparseInt(request.params.qtd)(request.params.qtd)
: 10;: 10;
letlet result = await result = await thisthis._service.getA._service.getAll(page, ll(page, qtd);qtd);
response.statresponse.status(200).json({ result us(200).json({ result });});
}} catchcatch (error) { (error) {
response.statresponse.status(500).json({ us(500).json({ error: error: error.message error.message ||||
error.toStrinerror.toString() g() });});
}}
}}
async getById(request: Request, response: Response) {async getById(request: Request, response: Response) {
trytry { {
constconst _id _id = request.params.id;= request.params.id;
letlet result = await result = await thisthis._service.get(_id);._service.get(_id);
response.statresponse.status(200).json({ result us(200).json({ result });});
}} catchcatch (error) { (error) {
response.statresponse.status(500).json({ error: us(500).json({ error: error.message ||error.message ||
error.toStrinerror.toString() g() });});
}}
}}
}}
exportexport defaultdefault newnew GaleriaControGaleriaController();ller();
Não temos nenhuma novidade na criação desses Não temos nenhuma novidade na criação desses arquivos, casoarquivos, caso
você compare comvocê compare com NewsControllerNewsController eles têm a mesma estrutura.eles têm a mesma estrutura.
Para que possamos testar todo Para que possamos testar todo o fluxo desenvolvido neste capítulo,o fluxo desenvolvido neste capítulo,
nós precisaremos criar as rotas de entrada das nossas novasnós precisaremos criar as rotas de entrada das nossas novas
controllers. Para isso, abra o controllers. Para isso, abra o seu arquivoseu arquivo startUp.tsstartUp.ts e atualize oe atualize o
métodométodo routesroutes dele com o seguinte trecho de código:dele com o seguinte trecho de código:
/*startUp.ts*//*startUp.ts*/
//Importação das novas controllers//Importação das novas controllers
importimport VideosController from VideosController from "./controller/videosController""./controller/videosController";;
importimport GaleriaController from GaleriaController from "./controller/galeriaController""./controller/galeriaController";;
//outras implementações//outras implementações
//método routes//método routes
routes() {routes() {
thisthis.app.route(.app.route("/""/").get((req, res) => {).get((req, res) => {
res.send({ versao:res.send({ versao: "0.0.2""0.0.2" });});
res.send({ versao:res.send({ versao: 0.0.20.0.2 }); });
});});
/*news*//*news*/
thisthis.app.route(.app.route("/api/v1/news/:page/:qtd""/api/v1/news/:page/:qtd").get((req:Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn NewsControlleNewsController.get(req, res);r.get(req, res);
});});
thisthis.app.route(.app.route("/api/v1/news/:id""/api/v1/news/:id").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn NewsControlleNewsController.getById(req, res);r.getById(req, res);
});});
/*videos*//*videos*/
thisthis.app.route(.app.route("/api/v1/videos/:page/:qtd""/api/v1/videos/:page/:qtd").get((req: Request,).get((req: Request,
res: Response) => {res: Response) => {
returnreturn VideosControlVideosController.get(req, res);ler.get(req, res);
});});
thisthis.app.route(.app.route("/api/v1/videos/:id""/api/v1/videos/:id").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn VideosControlVideosController.getById(reler.getById(req, q, res);res);
});});
/*galeria*//*galeria*/
thisthis.app.route(.app.route("/api/v1/galeria/:page/:qtd""/api/v1/galeria/:page/:qtd").get((req: Request,).get((req: Request,
res: Response) => {res: Response) => {
returnreturn GaleriaControGaleriaController.get(req, res);ller.get(req, res);
});});
thisthis.app.route(.app.route("/api/v1/galeria/:id""/api/v1/galeria/:id").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn GaleriaControGaleriaController.getById(rller.getById(req, eq, res);res);
});});
}}
Criando as novas collectionsCriando as novas collections
Com o código pronto, crie duas novas collections dentro da suaCom o código pronto, crie duas novas collections dentro da sua
basebase db portaldb portal uma chamadauma chamada videosvideos e outra chamadae outra chamada galeriasgalerias
basebase db_portaldb_portal , uma chamada, uma chamada videosvideos e outra chamadae outra chamada galeriasgalerias ,,
com os dados a seguir.com os dados a seguir.
Caso não se recorde de como criar uma collection ou de comoCaso não se recorde de como criar uma collection ou de como
atualizá-la, recomendo que leia novamente os capítulos 10 e 11atualizá-la, recomendo que leia novamente os capítulos 10 e 11
deste livrodeste livro..
/*videos*//*videos*/
{{
"titulo""titulo" : : "Anna Paula vence final do MasterChef 2020 e "Anna Paula vence final do MasterChef 2020 e dedica prêmiodedica prêmio
à filha"à filha",,
"texto""texto" : : "Cozinheira preparou um prato bem "Cozinheira preparou um prato bem brasileiro e se emocionoubrasileiro e se emocionou
ao levar grande prêmio da temporada"ao levar grande prêmio da temporada",,
"imagem""imagem" : : "https://thumb."https://thumb.mais.uol.com.bmais.uol.com.br/16886600-xlar/16886600-xlarge.jpg?ver=1"rge.jpg?ver=1",,
"duracao""duracao" : : "00:04:36""00:04:36",,
"link""link" : : "https://entre"https://entretenimento.band.tenimento.band.uol.com.br/viduol.com.br/videos/16886600/aeos/16886600/anna-nna-
paula-vence-fpaula-vence-final-do-mastercinal-do-masterchef-2020-e-dedhef-2020-e-dedica-premio-a-fica-premio-a-filha"ilha",,
"url""url" : : "https://play"https://player.mais.uol.coer.mais.uol.com.br/?mediaId=1m.br/?mediaId=16886600"6886600",,
"dataPublicacao""dataPublicacao" : : "2020-12-30T03:12:00.000""2020-12-30T03:12:00.000",,
"tags""tags" : : "masterchef, masterchef 2020""masterchef, masterchef 2020",,
"ativo""ativo" : true : true
}}
{{
"titulo""titulo" : : "Muito boa essa mousse de caramelo, diz Jacquin para"Muito boa essa mousse de caramelo, diz Jacquin para
Marina"Marina",,
"texto""texto" : : "Estudante fez uma tartelette de maçã com mousse da caramelo"Estudante fez uma tartelette de maçã com mousse da caramelo
salgado com alguns erros técnicos, mas causou uma boa impressão"salgado com alguns erros técnicos, mas causou uma boa impressão",,
"imagem""imagem" : : "https://thumb."https://thumb.mais.uol.com.bmais.uol.com.br/16886599-xlar/16886599-xlarge.jpg?ver=1"rge.jpg?ver=1",,
"duracao""duracao" : : "00:01:27""00:01:27",,
"link""link" : :
"https://entr"https://entretenimento.bandetenimento.band.uol.com.br/vi.uol.com.br/videos/16886599/deos/16886599/muito-boa-essa-muito-boa-essa-
mousse-de-carmousse-de-caramelo-diz-jacquamelo-diz-jacquin-para-marinain-para-marina"",,
"url""url" : : "https://play"https://player.mais.uol.coer.mais.uol.com.br/?mediaId=1m.br/?mediaId=16886599"6886599",,
"dataPublicacao""dataPublicacao" : : "2020-12-30T03:12:00.000""2020-12-30T03:12:00.000",,
"tags""tags" : : "masterchef, masterchef 2020""masterchef, masterchef 2020",,
"ativo""ativo" : true : true
}}
/*galeria*//*galeria*/
{{
"titulo""titulo":: "Marília Mendonça, Péricles, César Menotti e Maraísa em "Marília Mendonça, Péricles, César Menotti e Maraísa em especialespecial
de Natal"de Natal",,
"texto""texto":: "Especial de natal com "Especial de natal com celebridades no MasterChef 2020"celebridades no MasterChef 2020",,
"imagem""imagem":: "https://pubi"https://pubimg.band.uol.comg.band.uol.com.br/files/eb9m.br/files/eb9e64b485e94efc15e64b485e94efc15aa.jpg"aa.jpg",,
"link""link"::
"https://entr"https://entretenimento.bandetenimento.band.uol.com.br/ma.uol.com.br/masterchef/noticsterchef/noticias/16318389/maias/16318389/marilril
ia-mendonca-pia-mendonca-pericles-maraisaericles-maraisa-e-cesar-menot-e-cesar-menotti-participam-ti-participam-de-de-
%E2%80%9Cespecial-de-natal-do-masterchef"%E2%80%9Cespecial-de-natal-do-masterchef",,
"dataPublicacao""dataPublicacao":: "2020-08-12T10:26:00.00""2020-08-12T10:26:00.00",,
"tags""tags":: "masterchef, masterchef 2020""masterchef, masterchef 2020",,
"ativo""ativo": true,: true,
"fotos""fotos": [: [
[[
{{
"thumb""thumb":: "https://pubim"https://pubimg.band.uol.comg.band.uol.com.br/files/eb9e.br/files/eb9e64b485e94efc15a64b485e94efc15aa.jpg"a.jpg",,
"thumbNail""thumbNail"::
"https://pubi"https://pubimg.band.uol.commg.band.uol.com.br/files/eb9e.br/files/eb9e64b485e94efc1564b485e94efc15aa.jpg"aa.jpg",,
"credito""credito":: "Carlos Reinis/Band""Carlos Reinis/Band",,
"legenda""legenda":: "No especial de Natal, mais famosos entraram na cozinha do"No especial de Natal, mais famosos entraram na cozinha do
MasterChef"MasterChef"
}}
],],
[[
{{
"thumb""thumb":: "https://pubim"https://pubimg.band.uol.comg.band.uol.com.br/files/c41a.br/files/c41a8db03aa1f66c9358db03aa1f66c935e.jpg"e.jpg",,
"thumbNail""thumbNail"::
"https://pubi"https://pubimg.band.uol.commg.band.uol.com.br/files/c41a.br/files/c41a8db03aa1f66c938db03aa1f66c935e.jpg"5e.jpg",,
"credito""credito":: "Carlos Reinis/Band""Carlos Reinis/Band",,
"legenda""legenda":: "Foi a vez de "Foi a vez de César Menotti e Maraísa mostrarem seus dotesCésar Menotti e Maraísa mostrarem seus dotes
culinários"culinários"
}}
],],
[[
{{
"thumb""thumb":: "https://pubim"https://pubimg.band.uol.comg.band.uol.com.br/files/fad9.br/files/fad9cc5b705a0319508cc5b705a03195087.jpg"7.jpg",,
"thumbNail""thumbNail"::
"https://pubi"https://pubimg.band.uol.commg.band.uol.com.br/files/fad9.br/files/fad9cc5b705a031950cc5b705a03195087.jpg"87.jpg",,
"credito""credito":: "Carlos Reinis/Band""Carlos Reinis/Band",,
"legenda""legenda":: "Eles tiveram de enfrentar Péricles e Marília Mendonça no"Eles tiveram de enfrentar Péricles e Marília Mendonça no
desafio"desafio"
}}
}}
]]
]]
}}
12.3 Testando as novas rotas12.3 Testando as novas rotas
Para que possamos validar se Para que possamos validar se tudo está funcionando corretamente,tudo está funcionando corretamente,
execute o comandoexecute o comando npm run compilenpm run compile para fazer o transpile do seupara fazer o transpile do seu
projeto e depois execute o cprojeto e depois execute o comandoomando npm startnpm start ..
Começando a validação pela rota de vídeos, vamos seguir o mesmoComeçando a validação pela rota de vídeos, vamos seguir o mesmo
fluxo que fizemos para a validação da rota de notícias no capítulofluxo que fizemos para a validação da rota de notícias no capítulo
anterior.anterior.
Abra o seguinte Abra o seguinte endereço no seu endereço no seu navegador:navegador:http://localhost:5000/api/v1/videos/1/2http://localhost:5000/api/v1/videos/1/2
ResultadoResultado
{{
result: {result: {
Page: 1,Page: 1,
Qtd: 2,Qtd: 2,
Total: 2,Total: 2,
Data: [Data: [
{{
titulo:titulo: "Anna Paula vence final do MasterChef 2020 e "Anna Paula vence final do MasterChef 2020 e dedica prêmio àdedica prêmio à
filha"filha",,
texto:texto: "Cozinheira preparou um prato bem brasileiro e se emocionou ao"Cozinheira preparou um prato bem brasileiro e se emocionou ao
levar grande prêmio da levar grande prêmio da temporada"temporada",,
imagem:imagem: "https://thumb."https://thumb.mais.uol.com.bmais.uol.com.br/16886600-xlar/16886600-xlarge.jpg?ver=1"rge.jpg?ver=1",,
duracao:duracao: "00:04:36""00:04:36",,
link:link: "https://entre"https://entretenimento.band.tenimento.band.uol.com.br/viduol.com.br/videos/16886600/aeos/16886600/anna-paula-nna-paula-
vence-final-dvence-final-do-masterchef-20o-masterchef-2020-e-dedica-pr20-e-dedica-premio-a-filha"emio-a-filha",,
url:url: "https://play"https://player.mais.uol.coer.mais.uol.com.br/?mediaId=1m.br/?mediaId=16886600"6886600",,
dataPublicacao:dataPublicacao: "2020-12-30T03:12:00.000""2020-12-30T03:12:00.000",,
tags:tags: "masterchef, masterchef 2020""masterchef, masterchef 2020",,
ativo: true,ativo: true,
idid "6062740 066 94dd 6662 63""6062740 066 94dd 6662 63"
_id:_id: "6062740c066a94dda6662c63""6062740c066a94dda6662c63"
},},
{{
titulo:titulo: "Muito boa essa mousse de caramelo, diz Jacquin para Marina""Muito boa essa mousse de caramelo, diz Jacquin para Marina",,
texto:texto: "Estudante fez uma tartelette de maçã com mousse da caramelo"Estudante fez uma tartelette de maçã com mousse da caramelo
salgado com alguns erros técnicos, mas causou uma boa impressão"salgado com alguns erros técnicos, mas causou uma boa impressão",,
imagem:imagem: "https://thumb."https://thumb.mais.uol.com.bmais.uol.com.br/16886599-xlar/16886599-xlarge.jpg?ver=1"rge.jpg?ver=1",,
duracao:duracao: "00:01:27""00:01:27",,
link:link: "https://entre"https://entretenimento.band.tenimento.band.uol.com.br/viduol.com.br/videos/16886599/meos/16886599/muito-boa-uito-boa-
essa-mousse-dessa-mousse-de-caramelo-diz-e-caramelo-diz-jacquin-para-mjacquin-para-marina"arina",,
url:url: "https://play"https://player.mais.uol.coer.mais.uol.com.br/?mediaId=1m.br/?mediaId=16886599"6886599",,
dataPublicacao:dataPublicacao: "2020-12-30T03:12:00.000""2020-12-30T03:12:00.000",,
tags:tags: "masterchef, masterchef 2020""masterchef, masterchef 2020",,
ativo: true,ativo: true,
_id:_id: "6062740c066a94dda6662c64""6062740c066a94dda6662c64"
}}
]]
}}
}}
Agora testando Agora testando a listagem de gaa listagem de galerias, abra o seglerias, abra o seguinte endereço uinte endereço nono
seu navegador:seu navegador: http://localhost:5000/api/v1/galeria/1/10http://localhost:5000/api/v1/galeria/1/10
ResultadoResultado
{{
"result""result": {: {
"Page""Page": 1,: 1,
"Qtd""Qtd": 10,: 10,
"Total""Total": 1,: 1,
"Data""Data": [: [
{{
"titulo""titulo":: "Marília Mendonça, Péricles, César Menotti e Maraísa em "Marília Mendonça, Péricles, César Menotti e Maraísa em especialespecial
de Natal"de Natal",,
"chapeu""chapeu":: "MasterChef com celebridades""MasterChef com celebridades",,
"texto""texto":: "Especial de natal com "Especial de natal com celebridades no MasterChef 2020"celebridades no MasterChef 2020",,
"autor""autor":: "Da Redação""Da Redação",,
"imagem""imagem":: "https://pubi"https://pubimg.band.uol.comg.band.uol.com.br/files/eb9m.br/files/eb9e64b485e94efc15e64b485e94efc15aa.jpg"aa.jpg",,
"dataPublicacao""dataPublicacao":: "2020-08-12T10:26:00.00""2020-08-12T10:26:00.00",,
"ativo""ativo": true,: true,
"fotos""fotos": [: [
[[
[[
{{
"thumb""thumb":: "https://pubim"https://pubimg.band.uol.comg.band.uol.com.br/files/eb9e.br/files/eb9e64b485e94efc15a64b485e94efc15aa.jpg"a.jpg",,
"thumbNail""thumbNail"::
"https://pubi"https://pubimg.band.uol.commg.band.uol.com.br/files/eb9e.br/files/eb9e64b485e94efc1564b485e94efc15aa.jpg"aa.jpg",,
"credito""credito":: "Carlos Reinis/Band""Carlos Reinis/Band",,
"legenda""legenda":: "No especial de Natal, mais famosos entraram na cozinha do"No especial de Natal, mais famosos entraram na cozinha do
MasterChef"MasterChef"
}}
],],
[[
{{
"thumb""thumb":: "https://pubim"https://pubimg.band.uol.comg.band.uol.com.br/files/c41a.br/files/c41a8db03aa1f66c9358db03aa1f66c935e.jpg"e.jpg",,
"thumbNail""thumbNail"::
"https://pubi"https://pubimg.band.uol.commg.band.uol.com.br/files/c41a.br/files/c41a8db03aa1f66c938db03aa1f66c935e.jpg"5e.jpg",,
"credito""credito":: "Carlos Reinis/Band""Carlos Reinis/Band",,
"legenda""legenda":: "Foi a vez de "Foi a vez de César Menotti e Maraísa mostrarem seus dotesCésar Menotti e Maraísa mostrarem seus dotes
culinários"culinários"
}}
],],
[[
{{
"thumb""thumb":: "https://pubim"https://pubimg.band.uol.comg.band.uol.com.br/files/fad9.br/files/fad9cc5b705a0319508cc5b705a03195087.jpg"7.jpg",,
"thumbNail""thumbNail"::
"https://pubi"https://pubimg.band.uol.commg.band.uol.com.br/files/fad9.br/files/fad9cc5b705a031950cc5b705a03195087.jpg"87.jpg",,
"credito""credito":: "Carlos Reinis/Band""Carlos Reinis/Band",,
"legenda""legenda":: "Eles tiveram de enfrentar Péricles e Marília Mendonça no"Eles tiveram de enfrentar Péricles e Marília Mendonça no
desafio"desafio"
}}
]]
],],
"_id""_id":: "5fe7c2bb55d856158389a162""5fe7c2bb55d856158389a162",,
"link""link"::
"https://entr"https://entretenimento.bandetenimento.band.uol.com.br/ma.uol.com.br/masterchef/noticsterchef/noticias/16318389/maias/16318389/marilril
ia-mendonca-pia-mendonca-pericles-maraisaericles-maraisa-e-cesar-menot-e-cesar-menotti-participam-ti-participam-de-de-
%E2%80%9Cespecial-de-natal-do-masterchef"%E2%80%9Cespecial-de-natal-do-masterchef"
}}
]]
}}
}}
Analisando os re Analisando os retornos, note quetornos, note que, através de uma org, através de uma organizaçãoanização
utilizando conceitos simples como o da utilizando conceitos simples como o da classe genéricaclasse genérica Result<T>Result<T> ,,
nós conseguimos garantir uma estrutura de retorno padrão paranós conseguimos garantir uma estrutura de retorno padrão para
todas as nossas rotas.todas as nossas rotas.
Neste capítulo, nós reforçamos alguns conceitos que Neste capítulo, nós reforçamos alguns conceitos que nósnós
aprendemos no livro, como a importância de se tipar os nossosaprendemos no livro, como a importância de se tipar os nossos
dados, como e onde utilizardados, como e onde utilizar GenericsGenerics e vimos mais alguns exemplos e vimos mais alguns exemplos
de como a Orientação a Objetos deixa o nosso código maisde como a Orientação a Objetos deixa o nosso código mais
organizado.organizado.
Com isso, nós finalizamos mais este capítulo. No próximo, veremosCom isso, nós finalizamos mais este capítulo. No próximo, veremos
sobresobre Injeção de dependênciaInjeção de dependência, o que é e como , o que é e como aplicá-la no nossoaplicá-la no nosso
código.código.
C 13C 13
InjeçInjeção de Dão de D ependependênciaência
Neste capítulo, nós faremos uma Neste capítulo, nós faremos uma breve introdução à Injeção debreve introdução à Injeção de
Dependência e logo partiremos para a parte prática aplicando esseDependência e logo partiremos para a parte prática aplicando esse
padrão no nosso projeto desenvolvido nos capítulos anteriores.padrão no nosso projeto desenvolvido nos capítulos anteriores.
Aqui não será n Aqui não será necessário nos aprofuecessário nos aprofundar no que é ndar no que é Injeção deInjeção de
Dependência e nem nas formas que Dependência e nem nas formas que temos para trabalhar com ela.temos para trabalhar com ela.
VVamos passar amos passar rapidamente por alguns conceitos básicos rapidamente por alguns conceitos básicos para quepara que
você possa entender os benefícios de você possa entender os benefícios de utilizá-la e logo partiremosutilizá-la e logo partiremos
para opara o hands-onhands-on..Bom, mas o Bom, mas o que seria Injeção de Dependência?que seria Injeção de Dependência?
Injeção de Dependência é um padrão de Injeção de Dependência é um padrão de projeto utilizado para evitar projeto utilizado para evitar
o alto nível de o alto nível de acoplamento de código dentro de uma aplicação.acoplamento de código dentro de uma aplicação.
Para quem está tendo o seu primeiro contato comPara quem está tendo o seu primeiro contato com padrão de padrão de
projetos projetos (Design Patterns) neste livro, um padrão de projeto ou (Design Patterns) neste livro, um padrão de projeto ou
padrão de desenho é uma solução padrão de desenho é uma solução geral para um problema quegeral para um problema que
ocorre com frequência dentro de um ocorre com frequência dentro de um determinado contexto nodeterminado contexto no
desenvolvimento de software. Caso você tenha interesse em desenvolvimento de software. Caso você tenha interesse em saber saber
mais sobre esse assunto, eu recomendo a leitura do seguinte link:mais sobre esse assunto, eu recomendo a leitura do seguinte link:https://pt.wikipedhttps://pt.wikipedia.org/wiki/Padr%ia.org/wiki/Padr%C3%C3%A3o_de_A3o_de_projeto_dprojeto_de_softwaree_software
..
Para ficar mais claro o que seria acoplamento, vamos analisar aPara ficar mais claro o que seria acoplamento, vamos analisar a
nossa classenossa classe NewsControllerNewsController ..
/*newsController.ts*//*newsController.ts*/
classclass NewsController { NewsController {
privateprivate _service: _service: NewsService;NewsService;
constructorconstructor() {() {
constructorconstructor() {() {
thisthis._service =._service = newnew NewsService();NewsService();
}}
//outras implementações//outras implementações
Acredito que ne Acredito que nesse momento você deve esse momento você deve estar se perguntandstar se perguntando: oo: o
nosso código não está funcionando? nosso código não está funcionando? Analisando-o, vemos queAnalisando-o, vemos que
estamos instanciando uma classe, conforme aprendemos noestamos instanciando uma classe, conforme aprendemos no
capítulo 4 deste livro, e utilizando os seus métodos. Onde está ocapítulo 4 deste livro, e utilizando os seus métodos. Onde está o
erro?erro?
Em uma breve análise, essa classe está OK. O código compila e elaEm uma breve análise, essa classe está OK. O código compila e ela
está funcionando corretamente, mas note que ela tem está funcionando corretamente, mas note que ela tem uma instânciauma instância
dede NewsServiceNewsService . Conseguiu achar o problema?. Conseguiu achar o problema?
Caso não tenha encontrado, imagine o seguinte cenário: chegouCaso não tenha encontrado, imagine o seguinte cenário: chegou
uma nova demanda para adicionar um parâmetro ao uma nova demanda para adicionar um parâmetro ao construtor daconstrutor da
classeclasse NewsServiceNewsService . Perceba que isso afetará diretamente a classe. Perceba que isso afetará diretamente a classe
NewsControllerNewsController , que a está instanciando sem passar nenhum, que a está instanciando sem passar nenhum
parâmetro.parâmetro.
/*NewsService.ts*//*NewsService.ts*/
exportexport classclass NewsService NewsService implementsimplements INewsService { INewsService {
constructorconstructor(acomplamento: string) {(acomplamento: string) {
consoleconsole.log(acomplamento);.log(acomplamento);
}}
//outras implementações//outras implementações
/*Resultado*//*Resultado*/
controller/necontroller/newsController.tswsController.ts:10:25 - :10:25 - error TS2554: error TS2554: Expected 1Expected 1 argumentsarguments,,
but got 0.but got 0.
1010 thisthis._service =._service = newnew NewsService()NewsService();;
//outras implementações//outras implementações
Sistemas com alto acoplamento de código têm Sistemas com alto acoplamento de código têm os seguintesos seguintes
problemas:problemas:
Dificuldade no momento de Dificuldade no momento de manutenção;manutenção;
Dificuldade no momento de escrever os Dificuldade no momento de escrever os testes;testes;
;;
Insegurança no momento de desenvolver novasInsegurança no momento de desenvolver novas
funcionalidades.funcionalidades.
Esses são alguns dos problemas que nós conseguimos resolver Esses são alguns dos problemas que nós conseguimos resolver
adotando a utilização de Injeção de Dependência no nosso projeto.adotando a utilização de Injeção de Dependência no nosso projeto.
Para que você possa ter um melhor entendimento sobre essePara que você possa ter um melhor entendimento sobre esse
assunto, vamos alterar o nosso projeto para ver na prática quais sãoassunto, vamos alterar o nosso projeto para ver na prática quais são
os benefícios de se os benefícios de se trabalhar com Injeção de Dependência do seutrabalhar com Injeção de Dependência do seu
dia a dia.dia a dia.
13.1 Desacoplando o projeto13.1 Desacoplando o projeto
Avançando p Avançando para a parte práara a parte prática, vamos alterar o tica, vamos alterar o nosso código paranosso código para
remover os acoplamentos entre as nossas classes.remover os acoplamentos entre as nossas classes.
O primeiro passo será O primeiro passo será baixar uma biblioteca chamadabaixar uma biblioteca chamada tsyringetsyringe e e
uma outra chamadauma outra chamada reflect-metadatareflect-metadata. Para isso, abra um terminal. Para isso, abra um terminal
no seu computador, navegue até o seu projeto e digite o seguinteno seu computador, navegue até o seu projeto e digite o seguinte
comando nele:comando nele: npm i tsyringe reflect-metadata --savenpm i tsyringe reflect-metadata --save ..
tsyringe tsyringe : esse pacote nos permite trabalhar com injeção de: esse pacote nos permite trabalhar com injeção de
dependência.dependência.
reflect-metadreflect-metadataata : esse pacote nos permite trabalhar com os: esse pacote nos permite trabalhar com os
types em tempo de execução.types em tempo de execução.
Com os pacotes importados, o Com os pacotes importados, o próximo passo será configurá-los.próximo passo será configurá-los.
Crie um novo diretório chamadoCrie um novo diretório chamado sharedshared e, dentro dele, um arquivoe, dentro dele, um arquivo
chamadochamado container.tscontainer.ts ..
O arquivoO arquivo container.tscontainer.ts será responsável por registrar as será responsável por registrar as interfacesinterfaces
do nosso projeto:do nosso projeto: INewsServiceINewsService ,, IVideosServiceIVideosService ,, IGaleriaServiceIGaleriaService e ase as
classesclasses NewsServiceNewsService ,, VideosServiceVideosService ee GaleriaServiceGaleriaService , que estão, que estão
implementando essas interfaces.implementando essas interfaces.
pp
Atualize o seu a Atualize o seu arquivorquivo container.tscontainer.ts com o seguinte trecho decom o seguinte trecho de
código:código:
/*container.ts*//*container.ts*/
importimport "reflect-metadata""reflect-metadata";;
importimport { container } from { container } from 'tsyringe''tsyringe';;
importimport { GaleriaService } from { GaleriaService } from "../services/galeriaService""../services/galeriaService";;
importimport { NewsService } from { NewsService } from "../services/newsService""../services/newsService";;
importimport { VideosService } from { VideosService } from "../services/videosService""../services/videosService";;
container.register(container.register(
"INewsService""INewsService", {, {
useClass: NewsServiceuseClass: NewsService
},},
););
container.register(container.register(
"IVideosService""IVideosService", {, {
useClass: useClass: VideosServiceVideosService
},},
););
container.register(container.register(
"IGaleriaService""IGaleriaService", {, {
useClass: useClass: GaleriaServiceGaleriaService
},},
););
O próximo passo será O próximo passo será importar o contêinerimportar o contêiner, que criamos acima, , que criamos acima, nono
nosso arquivonosso arquivo startUp.tsstartUp.ts , para que ele seja carregado no nosso, para que ele seja carregado nonosso
projeto, e alterar a forma como estamos chamando nossos serviçosprojeto, e alterar a forma como estamos chamando nossos serviços
dentro do métododentro do método routes()routes() ..
/*startUp.ts*//*startUp.ts*/
importimport "reflect-metadata""reflect-metadata";;
importimport { NewsController } from { NewsController } from "./controller/newsController""./controller/newsController";;
importimport { VideosController } from { VideosController } from "./controller/videosController""./controller/videosController";;
importimport { GaleriaController } from { GaleriaController } from "./controller/galeriaController""./controller/galeriaController";;
i ti t { t i } f{ t i } f 't i ''t i '
importimport { container } from { container } from 'tsyringe''tsyringe';;
importimport './shared/container''./shared/container';;
classclass StartUp { StartUp {
//outras implementações//outras implementações
privateprivate news news = = container.rescontainer.resolve(NewsControlve(NewsController);oller);
privateprivate videos videos = = container.rescontainer.resolve(VideosConolve(VideosController);troller);
privateprivate galeria = galeria = container.rescontainer.resolve(GaleriaCoolve(GaleriaController);ntroller);
routes() {routes() {
//outras //outras implementaçõeimplementaçõess
/*news*//*news*/
thisthis.app.route(.app.route("/api/v1/news/:page/:qtd""/api/v1/news/:page/:qtd").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn thisthis.news.get(req, res);.news.get(req, res);
});});
thisthis.app.route(.app.route("/api/v1/news/:id""/api/v1/news/:id").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn thisthis.news.getById(.news.getById(req, req, res);res);
});});
/*videos*//*videos*/
thisthis.app.route(.app.route("/api/v1/videos/:page/:qtd""/api/v1/videos/:page/:qtd").get((req: Request,).get((req: Request,
res: Response) => {res: Response) => {
returnreturn thisthis.videos.get(re.videos.get(req, q, res);res);
});});
thisthis.app.route(.app.route("/api/v1/videos/:id""/api/v1/videos/:id").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn thisthis.videos.getByI.videos.getById(req, d(req, res);res);
});});
/*galeria*//*galeria*/
thisthis.app.route(.app.route("/api/v1/galeria/:page/:qtd""/api/v1/galeria/:page/:qtd").get((req: Request,).get((req: Request,
res: Response) => {res: Response) => {
returnreturn thisthis.galeria.get(r.galeria.get(req, eq, res);res);
})})
});});
thisthis.app.route(.app.route("/api/v1/galeria/:id""/api/v1/galeria/:id").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn thisthis.galeria.getBy.galeria.getById(req, Id(req, res);res);
});});
}}
}}
Analisando a cla Analisando a classesse StartUpStartUp nós temos:nós temos:
Na primeira linha do arquivo, estamos importando o Na primeira linha do arquivo, estamos importando o pacotepacote
reflect-metadreflect-metadataata . Esse import precisa estar no início do arquivo. Esse import precisa estar no início do arquivo
para que ele seja o primeiro a ser carregado;para que ele seja o primeiro a ser carregado;
Depois estamos importando o contêiner em que Depois estamos importando o contêiner em que nósnós
registramos as nossas interfaces antes da criação da classeregistramos as nossas interfaces antes da criação da classe
StartUp StartUp ;;
Em seguida, criamos três Em seguida, criamos três propriedades:propriedades: newsnews ,, videosvideos ee galeriagaleria ,,
e passamos para elas o valor da instância das controllers;e passamos para elas o valor da instância das controllers;
Por fim, nós removemos a chamada direta que estava nosPor fim, nós removemos a chamada direta que estava nos
métodos para as três métodos para as três propriedades que nós criamos:propriedades que nós criamos: newsnews ,,
videos videos ee galeriagaleria ..
Agora, caso você estej Agora, caso você esteja executando o seu a executando o seu transpile, no cotranspile, no console donsole do
Visual Studio Code, apareceram nove erros:Visual Studio Code, apareceram nove erros:
/* Erros de transpile*//* Erros de transpile*/
startUp.ts:5:1startUp.ts:5:10 - 0 - error TS2614: Moduleerror TS2614: Module '"./controller/newsController"''"./controller/newsController"'
has no exported memberhas no exported member 'NewsController''NewsController'. Did you mean to use. Did you mean to use 'import'import
NewsControlleNewsController r from from "./controlle"./controller/newsController/newsController"'r"' instead? instead?
55 importimport { NewsController } from { NewsController } from "./controller/newsController""./controller/newsController";;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
startUp.ts:6:startUp.ts:6:10 - 10 - error TS2614: Moduleerror TS2614: Module '"./controller/videosController"''"./controller/videosController"'
has no exported memberhas no exported member 'VideosController''VideosController'. Did you mean to use. Did you mean to use 'import'import
VideosControlVideosController ler from from "./controller"./controller/videosControl/videosController"'ler"' instead? instead?
66 importimport { VideosController } from { VideosController } from "./controller/videosController""./controller/videosController";;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
startUp.ts:7:startUp.ts:7:10 - 10 - error TS2614: Moduleerror TS2614: Module '"./controller/galeriaController"''"./controller/galeriaController"'
has no exported memberhas no exported member 'GaleriaController''GaleriaController'. Did you mean to use. Did you mean to use 'import'import
GaleriaControGaleriaController ller from from "./controller/"./controller/galeriaControllgaleriaController"'er"' instead? instead?
77 importimport { GaleriaController } from { GaleriaController } from "./controller/galeriaController""./controller/galeriaController";;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
startUp.ts:36startUp.ts:36:30 - :30 - error TS2339: Propertyerror TS2339: Property 'get''get' does not exist on type does not exist on type
'unknown''unknown'..
3636 returnreturn thisthis.news.get(req.news.get(req, , res);res);
~~~~~~
startUp.ts:41startUp.ts:41:30 - :30 - error TS2339: Propertyerror TS2339: Property 'getById''getById' does not exist on type does not exist on type
'unknown''unknown'..
4141 returnreturn thisthis.news.getById.news.getById(req, (req, res);res);
~~~~~~~~~~~~~~
startUp.ts:46startUp.ts:46:32 - :32 - error TS2339: Propertyerror TS2339: Property 'get''get' does not exist on type does not exist on type
'unknown''unknown'..
4646 returnreturn thisthis.videos.get(r.videos.get(req, eq, res);res);
~~~~~~
startUp.ts:50startUp.ts:50:32 - :32 - error TS2339: Propertyerror TS2339: Property 'getById''getById' does not exist on type does not exist on type
'unknown''unknown'..
5050 returnreturn thisthis.videos.getBy.videos.getById(req, Id(req, res);res);
~~~~~~~~~~~~~~
startUp.ts:55startUp.ts:55:33 - :33 - error TS2339: Propertyerror TS2339: Property 'get''get' does not exist on type does not exist on type
'unknown''unknown'..
5555 returnreturn thisthis.galeria.get(.galeria.get(req, req, res);res);
~~~~~~
startUp.ts:59startUp.ts:59:33 - :33 - error TS2339: Propertyerror TS2339: Property 'getById''getById' does not exist on type does not exist on type
'unknown''unknown'..
5959 tt thithi l i tBl i tB Id(Id( ))
5959 returnreturn thisthis.galeria.getB.galeria.getById(req, yId(req, res);res);
Esses erros estão dizendo que nós não podemos mais exportar asEsses erros estão dizendo que nós não podemos mais exportar as
nossas controllers instanciando-as e que devemos atualizar o nossas controllers instanciando-as e que devemos atualizar o nossonosso
arquivoarquivo tsconfig.jsontsconfig.json para que possamos trabalhar com ospara que possamos trabalhar com os
decorators. Caso você não se lembre do que são os decorators, eudecorators. Caso você não se lembre do que sãoos decorators, eu
recomendo que volte ao capítulo 7 recomendo que volte ao capítulo 7 deste livro.deste livro.
13.2 Decorators na prática13.2 Decorators na prática
Para que possamos trabalhar com os Para que possamos trabalhar com os decorators, nós precisamosdecorators, nós precisamos
adicionar as propriedades a seguir ao arquivoadicionar as propriedades a seguir ao arquivo tsconfig.jsontsconfig.json ::
"compilerOptions""compilerOptions": {: {
//outras implementações//outras implementações
"experimentalDecorators""experimentalDecorators": true,: true,
"emitDecoratorMetadata""emitDecoratorMetadata": true,: true,
//outras implementações//outras implementações
}}
Vamos atualizar os arquivosVamos atualizar os arquivos newsController.tsnewsController.ts ,, videosController.tsvideosController.ts ee
galeriaController.tsgaleriaController.ts para resolver o problema com opara resolver o problema com o exportexport , que, que
está aparecendo na console do Visual Studio Code.está aparecendo na console do Visual Studio Code.
/*newsController.ts*//*newsController.ts*/
importimport { injectable, inject } from { injectable, inject } from "tsyringe""tsyringe";;
importimport { INewsService } from { INewsService } from "../contracts/iNewsService""../contracts/iNewsService";;
//outras implementações//outras implementações
@injectable()@injectable()
exportexport classclass NewsController { NewsController {
constructorconstructor(@inject('INews(@inject('INewsService') private _service: Service') private _service: INewsService) {INewsService) {
}}
//outras implementações//outras implementações
//Remover export default new//Remover export default new NewsController(NewsController(););
//Remover export default new //Remover export default new NewsController(NewsController(););
}}
/*videosController.ts*//*videosController.ts*/
importimport { injectable, inject } from { injectable, inject } from "tsyringe""tsyringe";;
importimport { IVideosService } from { IVideosService } from "../contracts/iVideosService""../contracts/iVideosService";;
//outras implementações//outras implementações
@injectable()@injectable()
exportexport classclass VideosController { VideosController {
constructorconstructor(@inject('IVide(@inject('IVideosService') private osService') private _service:_service:
IVideosServicIVideosService) e) {}{}
//outras //outras implementaçõeimplementaçõess
//Remover export default new //Remover export default new VideosControlleVideosController();r();
}}
/*galeriaController.ts*//*galeriaController.ts*/
importimport { injectable, inject } from { injectable, inject } from "tsyringe""tsyringe";;
importimport { IGaleriaService } from { IGaleriaService } from "../contracts/iGaleriaService""../contracts/iGaleriaService";;
@injectable()@injectable()
exportexport classclass GaleriaController { GaleriaController {
constructorconstructor(@inject('IGale(@inject('IGaleriaService') private riaService') private _service:_service:
IGaleriaServiIGaleriaService) ce) {}{}
//outras //outras implementaçõeimplementaçõess
//Remover export default //Remover export default new GaleriaController();new GaleriaController();
}}
VVamos analisar os amos analisar os três arquivos que três arquivos que nós atualizamos:nós atualizamos:
Removemos a última linha que os estava exportando como umaRemovemos a última linha que os estava exportando como uma
nova instância e passamos a nova instância e passamos a exportá-los direto, deixando aexportá-los direto, deixando a
parte da instância para a Injeção parte da instância para a Injeção de Dependência;de Dependência;
Em seguida, nós adicionamos dois decorators, um de classe, oEm seguida, nós adicionamos dois decorators, um de classe, o
@injectable()@injectable() , e um outro de parâmetro no construtor, o, e um outro de parâmetro no construtor, o
@inject('') @inject('') ..
13.3 Testando o projeto13.3 Testando o projeto
Para que possamos testar se Para que possamos testar se o nosso projeto continua funcionandoo nosso projeto continua funcionando
corretamente, vamos validar todas as rotas corretamente, vamos validar todas as rotas desenvolvidas nosdesenvolvidas nos
capítulos anteriores.capítulos anteriores.
VVamos começar amos começar pelas rotas de pelas rotas de notícias:notícias:
/*News/*News
rota: rota: http://localhoshttp://localhost:5000/api/v1/t:5000/api/v1/news/1/1news/1/1
Resultado:Resultado:
*/*/
{{
"result""result": {: {
"Page""Page": 1,: 1,
"Qtd""Qtd": 1,: 1,
"Total""Total": 2,: 2,
"Data""Data": [: [
{{
"_id""_id":: "5fe93c5555d856158389a165""5fe93c5555d856158389a165",,
"chapeu""chapeu":: "vitória no MasterChef""vitória no MasterChef",,
"titulo""titulo":: "'O grito saiu da garganta', conta Danielle após rever vitória"'O grito saiu da garganta', conta Danielle após rever vitória
no MasterChef"no MasterChef",,
"texto""texto":: "Vencedora do sétimo episódio do MasterChef Brasil 2020, a"Vencedora do sétimo episódio do MasterChef Brasil 2020, a
economista Danielle comentou os melhores momentos de economista Danielle comentou os melhores momentos de sua passagem pelasua passagem pela
cozinha mais famosa do país. A campeã disse que cozinha mais famosa do país. A campeã disse que a vitória coroou suaa vitória coroou sua
dedicação à Gastronomia e que vencer o programa mudou a dedicação à Gastronomia e que vencer o programa mudou a sua vidasua vida
profissional. “O grito saiu da garganta”, contou após rever a decisão dosprofissional. “O grito saiu da garganta”, contou após rever a decisão dos
jurados."jurados.",,
"autor""autor":: "Da Redação""Da Redação",,
"imagem""imagem":: "https://imag"https://imagem.band.com.brem.band.com.br/novahome/451a/novahome/451a72ca-e766-4422-72ca-e766-4422-81c2-81c2-
fa2f0a09e2d2.jpg"fa2f0a09e2d2.jpg",,
"link""link"::
"https://entr"https://entretenimento.bandetenimento.band.uol.com.br/ma.uol.com.br/masterchef/noticsterchef/noticias/16308993/--ias/16308993/--o-o-
grito-saiu-dagrito-saiu-da-garganta---con-garganta---conta-danielle-apta-danielle-apos-rever-vitoros-rever-vitoria-no-mastercheia-no-masterchef"f",,
"dataPublicacao""dataPublicacao":: "2020-08-28T17:50:43.653Z""2020-08-28T17:50:43.653Z",,
"ativo""ativo": true: true
}}
]]
]]
}}
}}
/*Videos/*Videos
http://localhhttp://localhost:5000/api/v1ost:5000/api/v1/videos/1/1/videos/1/1
Resultado:Resultado:
*/*/
{{
"result""result": {: {
"Page""Page": 1,: 1,
"Qtd""Qtd": 1,: 1,
"Total""Total": 2,: 2,
"Data""Data": [: [
{{
"titulo""titulo":: "Péricles e Marília Mendonça vencem MasterChef especial de"Péricles e Marília Mendonça vencem MasterChef especial de
Natal"Natal",,
"chapeu""chapeu":: "Masterchef 2020""Masterchef 2020",,
"texto""texto":: "Amigos de longa data, os dois cantores mostraram que formam uma"Amigos de longa data, os dois cantores mostraram que formam uma
boa dupla também na cozinha. Eles prepararam lombo suíno, risoto de uva-boa dupla também na cozinha. Eles prepararam lombo suíno, risoto de uva-
passa e manjar de coco."passa e manjar de coco.",,
"autor""autor":: "Da Redação""Da Redação",,
"imagem""imagem":: "http://thumb"http://thumb.mais.uol.com..mais.uol.com.br/16885455-xlbr/16885455-xlarge.jpg?ver=0"arge.jpg?ver=0",,
"dataPublicacao""dataPublicacao":: "2020-12-24T03:12:00.000""2020-12-24T03:12:00.000",,
"duracao""duracao":: "00:02:39""00:02:39",,
"tags""tags":: "masterchef, masterchef 2020""masterchef, masterchef 2020",,
"ativo""ativo": true,: true,
"_id""_id":: "5fe943e755d856158389a167""5fe943e755d856158389a167",,
"link""link":: "https://player.mais.uol.com.br/?"https://player.mais.uol.com.br/?
mediaId=16885mediaId=16885455&autopla455&autoplay=false&shy=false&share=false&are=false&startHd=720p&amstartHd=720p&rp;r
elated=false"elated=false"
}}
]]
}}
}}
/*Galeria/*Galeria
http://localhhttp://localhost:5000/api/v1ost:5000/api/v1/galeria/1/1/galeria/1/1
Resultado:Resultado:
*/*/
{{
"result""result": {: {
{{resultresult : {: {
"Page""Page": 1,: 1,
"Qtd""Qtd": 1,: 1,
"Total""Total": 1,: 1,
"Data""Data": [: [
{{
"titulo""titulo":: "Marília Mendonça, Péricles, César Menotti e Maraísa em "Marília Mendonça, Péricles, César Menotti e Maraísa em especialespecial
de Natal"de Natal",,
"chapeu""chapeu":: "MasterChef com celebridades edição de Natal""MasterChef com celebridades edição de Natal",,
"texto""texto":: "Especial de natal com "Especial de natal com celebridades no MasterChef 2020"celebridades no MasterChef 2020",,
"autor""autor":: "Da Redação""Da Redação",,
"imagem""imagem":: "https://pubi"https://pubimg.band.uol.comg.band.uol.com.br/files/eb9m.br/files/eb9e64b485e94efc15e64b485e94efc15aa.jpg"aa.jpg",,
"dataPublicacao""dataPublicacao":: "2020-08-12T10:26:00.00""2020-08-12T10:26:00.00",,
"ativo""ativo": true,: true,
"fotos""fotos": [: [
[[
{{
"thumb""thumb":: "https://pubim"https://pubimg.band.uol.comg.band.uol.com.br/files/eb9e.br/files/eb9e64b485e94efc15a64b485e94efc15aa.jpg"a.jpg",,
"thumbNail""thumbNail"::
"https://pubi"https://pubimg.band.uol.commg.band.uol.com.br/files/eb9e.br/files/eb9e64b485e94efc1564b485e94efc15aa.jpg"aa.jpg",,
"credito""credito":: "Carlos Reinis/Band""Carlos Reinis/Band",,
"legenda""legenda":: "No especial de Natal, mais famosos entraram na cozinha do"No especial de Natal, mais famosos entraram na cozinha do
MasterChef"MasterChef"
}}
],],
[[
{{
"thumb""thumb":: "https://pubim"https://pubimg.band.uol.comg.band.uol.com.br/files/c41a.br/files/c41a8db03aa1f66c9358db03aa1f66c935e.jpg"e.jpg",,
"thumbNail""thumbNail"::
"https://pubi"https://pubimg.band.uol.commg.band.uol.com.br/files/c41a.br/files/c41a8db03aa1f66c938db03aa1f66c935e.jpg"5e.jpg",,
"credito""credito":: "Carlos Reinis/Band""Carlos Reinis/Band",,
"legenda""legenda":: "Foi a vez de "Foi a vez de César Menotti e Maraísa mostrarem seus dotesCésar Menotti e Maraísa mostrarem seus dotes
culinários"culinários"
}}
],],
[[
{{
"thumb""thumb":: "https://pubim"https://pubimg.band.uol.comg.band.uol.com.br/files/fad9.br/files/fad9cc5b705a0319508cc5b705a03195087.jpg"7.jpg",,
"thumbNail""thumbNail"::
"https://pubi"https://pubimg.band.uol.commg.band.uol.com.br/files/fad9.br/files/fad9cc5b705a031950cc5b705a03195087.jpg"87.jpg",,
"credito""credito":: "Carlos Reinis/Band""Carlos Reinis/Band",,
// ,,
"legenda""legenda":: "Eles tiveram de enfrentar Péricles e Marília Mendonça no"Eles tiveram de enfrentar Péricles e Marília Mendonça no
desafio"desafio"
}}
]]
],],
"_id""_id":: "5fe9442055d856158389a169""5fe9442055d856158389a169",,
"link""link"::
"https://entr"https://entretenimento.bandetenimento.band.uol.com.br/ma.uol.com.br/masterchef/noticsterchef/noticias/16318389/maias/16318389/marilril
ia-mendonca-pia-mendonca-pericles-maraisaericles-maraisa-e-cesar-menot-e-cesar-menotti-participam-ti-participam-de-de-
%E2%80%9Cespecial-de-natal-do-masterchef"%E2%80%9Cespecial-de-natal-do-masterchef"
}}
]]
}}
}}
Antes de finali Antes de finalizar este capítulo, ezar este capítulo, eu gostaria de sugu gostaria de sugerir um desafio. :erir um desafio. :))
Imagine o seguinte cenário: chegou uma nova demanda paraImagine o seguinte cenário: chegou uma nova demanda para
criarmos uma nova rota criarmos uma nova rota de listagem de podcasts. Tde listagem de podcasts. Tendo em mente oendo em mente o
que desenvolvemos até aqui, quais seriam os passos para a criaçãoque desenvolvemos até aqui, quais seriam os passos para a criação
dessa nova rota? (Caso fique em dúvida referente aos atributos dedessa nova rota? (Caso fique em dúvida referente aos atributos de
um podcast, ele tem os mesmos atributos da model de vídeos).um podcast, ele tem os mesmos atributos da model de vídeos).
A seguir A seguir, você tem o link do , você tem o link do projeto no meu projeto no meu GitHGitH ub com todo oub com todo o
código que nós desenvolvemos até este capítulo do livro mais acódigo que nós desenvolvemos até este capítulo do livro mais a
implementação da nossa nova demanda de podcast:implementação da nossa nova demanda de podcast:
https://github.cohttps://github.com/programadrianom/programadriano/api_mc_/api_mc_livro/tree/capitullivro/tree/capitulo-13o-13
Com isso, finalizamos mais este capítulo. No próximo, nósCom isso, finalizamos mais este capítulo. No próximo, nósdocumentaremos o nosso projeto.documentaremos o nosso projeto.
C 14C 14
DDocumeocumentando o projntando o projetoeto
Para finalizar o fluxo de Para finalizar o fluxo de desenvolvimento do nosso projeto, odesenvolvimento do nosso projeto, o
próximo passo será documentá-lo. Para isso, próximo passo será documentá-lo. Para isso, nós utilizaremos onós utilizaremos o
JSDoc.JSDoc.
Caso não se recorde do JSDoc, trata-se de uma linguagem deCaso não se recorde do JSDoc, trata-se de uma linguagem de
marcação que nós podemos utilizar para marcação que nós podemos utilizar para documentar códigodocumentar código
JavaScript. Ele nos permite usar JavaScript. Ele nos permite usar comentários para fornecer comentários para fornecer
informações sobre elementos de código, como funções, campos informações sobre elementos de código, como funções, campos ee
variáveis.variáveis.
14.1 Organizando o projeto14.1 Organizando o projeto
Antes de document Antes de documentar o nosso projetoar o nosso projeto, que tal fazer u, que tal fazer um ajuste nele?m ajuste nele?
Analisando a n Analisando a nossa classeossa classe StartUpStartUp , observe que o método, observe que o método routes()routes()
está com quase 40 linhas de código e, caso chegue uma novaestá com quase 40 linhas de código e, caso chegue uma nova
demanda para criarmos uma nova rota no nosso projeto, essedemanda para criarmos uma nova rota no nosso projeto, esse
método só tende a aumentar.método só tende a aumentar.
Para resolver esse problema de quantidade de linhas, nós Para resolver esse problema de quantidade de linhas, nós podemospodemos
migrar as chamadas das rotas de dentro desse método para umamigrar as chamadas das rotas de dentro desse método para uma
outra camada do nosso projeto. Esse processo, além de diminuir aoutra camada do nosso projeto. Esse processo, além de diminuir a
quantidade de linhas de código do nosso método, deixará quantidade de linhas de código do nosso método, deixará o nossoo nosso
projeto mais organizado.projeto mais organizado.
Ajustando a classe StartUpAjustando a classe StartUp
Crie um novo diretório na raiz do seu projeto chamadoCrie um novo diretório na raiz do seu projeto chamado routerrouter ee
dentro dele crie quatro arquivos:dentro dele crie quatro arquivos:
newsRouter.tsnewsRouter.ts videosRouter.tsvideosRouter.ts
l il i e a nova rotae a nova rota dd que criamos noque criamos no
dentro dele crie quatro arquivos:dentro dele crie quatro arquivos: ,, ,,galeriaRouter.tsgaleriaRouter.ts e a nova rotae a nova rota podcastRouter.tspodcastRouter.ts , que criamos no, que criamos no
capítulo anterior para podcasts.capítulo anterior para podcasts.
Iniciando pelo arquivoIniciando pelo arquivo newsRouter.tsnewsRouter.ts , vamos copiar para ele as, vamos copiar para ele as
dependências da nossa rotadependências da nossa rota /news/news que estão dentro do arquivoque estão dentro do arquivo
startUp.tsstartUp.ts ::
//newsRouter.ts//newsRouter.ts
importimport "reflect-metadata""reflect-metadata";;
importimport express, { Request, Response } from express, { Request, Response } from "express""express";;
importimport { container } from { container } from "tsyringe""tsyringe";;
importimport { NewsController } from { NewsController } from "../controller/newsController""../controller/newsController";;
constconst newsRouter = newsRouter = express();express();
constconst news news = container.resolve(NewsContr= container.resolve(NewsController);oller);
newsRouter.route(newsRouter.route("/api/v1/news/:page/:qtd""/api/v1/news/:page/:qtd").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturnnews.get(req, res); news.get(req, res);
});});
newsRouter.route(newsRouter.route("/api/v1/news/:id""/api/v1/news/:id").get((req: Request, res: Response) =>).get((req: Request, res: Response) =>
{{
returnreturn news.getById(req, res); news.getById(req, res);
});});
exportexport defaultdefault newsRouter; newsRouter;
Analisando o a Analisando o arquivorquivo newsRouter.tsnewsRouter.ts , temos:, temos:
No início dele nós copiamos os pacotes da classeNo início dele nós copiamos os pacotes da classe StartUpStartUp ::
reflect-metadreflect-metadataata ,, expressexpress ,, tsyringetsyringe e a classee a classe NewsControllerNewsController ;;
Em seguida, nós criamos Em seguida, nós criamos uma constante chamadauma constante chamada newsRouternewsRouter ,,
recebendo o pacoterecebendo o pacote express()express() ;;
Depois, criamos uma constante chamadaDepois, criamos uma constante chamada newsnews e passamos ae passamos a
instância deinstância de NewsControllerNewsController para ela;para ela;
Por fim, copiamos as chamadas das nossas rotas e Por fim, copiamos as chamadas das nossas rotas e exportamosexportamos
a constantea constante newsRouternewsRouter para que possamos importá-la na classepara que possamos importá-la na classe
StartUp StartUp ..
Agora que já Agora que já sabemos como criar o nosabemos como criar o nosso arquivo de rotas, sso arquivo de rotas, atualizeatualize
os arquivosos arquivos videosRouter.tsvideosRouter.ts ,, galeriaRouter.tsgaleriaRouter.ts ee podcastRouter.tspodcastRouter.ts comcom
os seguintes trechos de código:os seguintes trechos de código:
//videosRouter.ts//videosRouter.ts
importimport "reflect-metadata""reflect-metadata";;
importimport express, { Request, Response } from express, { Request, Response } from "express""express";;
importimport { container } from { container } from "tsyringe""tsyringe";;
importimport { VideosController } from { VideosController } from "../controller/videosController""../controller/videosController";;
constconst videosRouter = express(); videosRouter = express();
constconst videos = videos = container.rescontainer.resolve(VideosConolve(VideosController);troller);
videosRouter.route(videosRouter.route("/api/v1/videos/:page/:qtd""/api/v1/videos/:page/:qtd").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn videos.get(req, res); videos.get(req, res);
});});
videosRouter.route(videosRouter.route("/api/v1/videos/:id""/api/v1/videos/:id").get((req: Request, res: Response)).get((req: Request, res: Response)
=> {=> {
returnreturn videos.getById(req, res); videos.getById(req, res);
});});
exportexport defaultdefault videosRouter; videosRouter;
//galeriaRouter.ts//galeriaRouter.ts
importimport "reflect-metadata""reflect-metadata";;
importimport express, { Request, Response } from express, { Request, Response } from "express""express";;
importimport { container } from { container } from "tsyringe""tsyringe";;
importimport { GaleriaController } from { GaleriaController } from "../controller/galeriaController""../controller/galeriaController";;
constconst galeriaRouter = express(); galeriaRouter = express();
constconst galeria = galeria = container.resocontainer.resolve(GaleriaConlve(GaleriaController);troller);
galeriaRouter.route(galeriaRouter.route("/api/v1/galeria/:page/:qtd""/api/v1/galeria/:page/:qtd").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn galeria.get(req, res); galeria.get(req, res);
});});
galeriaRouter.route(galeriaRouter.route("/api/v1/galeria/:id""/api/v1/galeria/:id").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
tt l i tB Id( )l i tB Id( )
returnreturn galeria.getById(req, res); galeria.getById(req, res);
});});
exportexport defaultdefault galeriaRouter; galeriaRouter;
//podcastRouter.ts//podcastRouter.ts
importimport "reflect-metadata""reflect-metadata";;
importimport express, { Request, Response } from express, { Request, Response } from "express""express";;
importimport { container } from { container } from "tsyringe""tsyringe";;
importimport { PodcastController } from { PodcastController } from "../controller/podcastController""../controller/podcastController";;
constconst podcastRouter = express(); podcastRouter = express();
constconst podcast = podcast = container.resocontainer.resolve(PodcastConlve(PodcastController);troller);
podcastRouter.route(podcastRouter.route("/api/v1/podcast/:page/:qtd""/api/v1/podcast/:page/:qtd").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn podcast.get(req, res); podcast.get(req, res);
});});
podcastRouter.route(podcastRouter.route("/api/v1/podcast/:id""/api/v1/podcast/:id").get((req: Request, res:).get((req: Request, res:
Response) => {Response) => {
returnreturn podcast.getById(req, res); podcast.getById(req, res);
});});
exportexport defaultdefault podcastRouter; podcastRouter;
O próximo passo será remover as chamadas da classeO próximo passo será remover as chamadas da classe StartUpStartUp queque
nós copiamos para os arquivosnós copiamos para os arquivos newsRouter.tsnewsRouter.ts ,, videosRouter.tsvideosRouter.ts ,,
galeriaRouter.tsgaleriaRouter.ts ee podcastRouter.tspodcastRouter.ts e adicionar essas novase adicionar essas novas
chamadas dentro do métodochamadas dentro do método routes()routes() ..
A seguir você tem o A seguir você tem o arquivoarquivo startUp.tsstartUp.ts atualizado:atualizado:
//startUp.ts atualizada//startUp.ts atualizada
importimport express, { Application } from express, { Application } from "express""express";;
importimport database from database from "./infra/db""./infra/db";;
importimport './shared/container''./shared/container';;
importimport newsRouter from newsRouter from "./router/newsRouter""./router/newsRouter";;
importimport videosRouter from videosRouter from "./router/videosRouter""./router/videosRouter";;
importimport galeriaRouter fromgaleriaRouter from " /router/galeriaRouter"" /router/galeriaRouter";;
importimport galeriaRouter from galeriaRouter from ./router/galeriaRouter./router/galeriaRouter ;;
importimport podcastRouter from podcastRouter from "./router/podcastRouter""./router/podcastRouter";;
classclass StartUp { StartUp {
publicpublic app: Application; app: Application;
privateprivate _db: database = _db: database = newnew database(); database();
constructorconstructor() {() {
thisthis.app = express();.app = express();
thisthis._db.createConnection();._db.createConnection();
thisthis.routes();.routes();
}}
routes() {routes() {
thisthis.app.route(.app.route("/""/").get((req, res) => {).get((req, res) => {
res.send({ versao:res.send({ versao: "0.0.2""0.0.2" }); });
});});
thisthis.app.use(.app.use("/""/", newsRouter);, newsRouter);
thisthis.app.use(.app.use("/""/", , videosRouter)videosRouter);;
thisthis.app.use(.app.use("/""/", , galeriaRoutergaleriaRouter););
thisthis.app.use(.app.use("/""/", , podcastRouterpodcastRouter););
}}
}}
exportexport defaultdefault newnew StartUp(); StartUp();
Bem mais organizado, certo?Bem mais organizado, certo?
A nossa classe A nossa classe StartUpStartUp tinha aproximadamente 73 linhas de códigotinha aproximadamente 73 linhas de códigoe depois do nosso e depois do nosso ajuste ela ficou com ajuste ela ficou com aproximadamente 33 linhas.aproximadamente 33 linhas.
14.2 Documentando o nosso código14.2 Documentando o nosso código
A prática de ad A prática de adicionar comentários aicionar comentários ao código ajuda o código ajuda no entendimenno entendimentoto
de outros desenvolvedores no momento de uma de outros desenvolvedores no momento de uma possívelpossível
manutenção ou implementação de novas manutenção ou implementação de novas funcionalidadefuncionalidades.s.
Conforme mencionado no início deste capítulo, nós utilizaremos oConforme mencionado no início deste capítulo, nós utilizaremos o
JSDoc para documentar ocódigo.JSDoc para documentar o código.
A seguir A seguir, você tem algumas da, você tem algumas das marcações que ele pes marcações que ele permitermite
adicionar ao nosso código:adicionar ao nosso código:
@deprecated @deprecated : especifica uma função ou : especifica uma função ou um método preterido.um método preterido.
@description @description : especifica a descrição de uma função ou um: especifica a descrição de uma função ou um
método.método.
@param @param : especifica informações para um parâmetro em uma: especifica informações para um parâmetro em uma
função ou método. O TypeScript também dá suporte àfunção ou método. O TypeScript também dá suporte à
marcaçãomarcação @paramTag@paramTag ..
@property @property : especifica informações, incluindo uma descrição,: especifica informações, incluindo uma descrição,
para um campo ou membro definido em um objeto.para um campo ou membro definido em um objeto.
@returns @returns : especifica um valor de retorno.: especifica um valor de retorno. @summary @summary : especifica a descrição de uma função ou método.: especifica a descrição de uma função ou método.
@type @type : especifica o tipo para uma constante ou uma variável.: especifica o tipo para uma constante ou uma variável.
@typedef @typedef : especifica um tipo : especifica um tipo personalizado.personalizado.
Para que você possa reforçar tudo o que aprendeu nos capítulos 11,Para que você possa reforçar tudo o que aprendeu nos capítulos 11,
12 e 13 deste livro, que tal um12 e 13 deste livro, que tal um overviewoverview de todos os passos que nós de todos os passos que nós
demos para a construção da nossa API?demos para a construção da nossa API?
Assim conseguimos enten Assim conseguimos entender melhor cada eder melhor cada etapa desenvolvida tapa desenvolvida e,e,
conforme a necessidade, podemos conforme a necessidade, podemos documentá-las.documentá-las.
ModelsModels
Conforme nós aprendemos no capítulo 1Conforme nós aprendemos no capítulo 11 deste livro, as 1 deste livro, as models sãomodels são
a representação de um conjunto de a representação de um conjunto de informações sobre determinadoinformações sobre determinado
conceito do sistema. Toda model possui atributos, que são asconceito do sistema. Toda model possui atributos, que são as
informações que a referenciam.informações que a referenciam.
Para nossa API, nós criamos as seguintes models:Para nossa API, nós criamos as seguintes models: NewsNews ,, VideosVideos ,,
GaleriaGaleria ,, PodcastPodcast ,, FotosFotos e todas elas herdam os atributos dee todas elas herdam os atributos de CoreCore ..
I i i d l lI i i d l l t li i t t h dt li i t t h d
Iniciando pela classeIniciando pela classe CoreCore , atualize-a com o seguinte trecho de, atualize-a com o seguinte trecho de
código:código:
importimport { Document } from { Document } from 'mongoose''mongoose';;
/**/**
* @summary Classe abstrata para criação das models* @summary Classe abstrata para criação das models
* @type titulo {String} título* @type titulo {String} título
* @ty* @type tepe texto xto {String} {String} texto texto ou dou descriçãoescrição
* @type imagem {String} imagem default ou avatar* @type imagem {String} imagem default ou avatar
* @type dataPublicacao {Date} data * @type dataPublicacao {Date} data de publicaçãode publicação
* @type tags {String} tags relacionada a model* @type tags {String} tags relacionada a model
* @type link * @type link {String} link do conteudo Ex.: {String} link do conteudo Ex.: https://conteuhttps://conteudo.com.brdo.com.br
* @type ativo {Boolean} status* @type ativo {Boolean} status
*/*/
exportexport abstract abstract classclass Core extends Document { Core extends Document {
titulo:titulo: StringString;;
texto:texto: StringString;;
imagem:imagem: StringString;;
dataPublicacao:dataPublicacao: DateDate;;
tags:tags: StringString;;
link:link: StringString;;
ativo:ativo: BooleanBoolean;;
}}
Seguindo o mesmo modelo que nós Seguindo o mesmo modelo que nós utilizamos para documentar autilizamos para documentar a
classeclasse CoreCore , atualize as classes, atualize as classes NewsNews ,, VideosVideos ,, GaleriaGaleria ,, PodcastPodcast ,,
FotosFotos com os seguintes trechos de código.com os seguintes trechos de código.
//News//News
importimport { Core } from { Core } from "./core""./core";;
/**/**
* Model de news* Model de news
* @type chapeu {String} titulo menor* @type chapeu {String} titulo menor
* @type autor {String} quem escreveu a noticia* @type autor {String} quem escreveu a noticia
*/*/
exportexport classclass News extends Core { News extends Core {
chapeu:chapeu: StringString;;
autor:autor: StringString;;
}}
//Videos//Videos
importimport { Core } from { Core } from "./core""./core";;
/**/**
* Model de video* Model de video
* @type url {String} url do * @type url {String} url do videovideo
* @type duração {String} tempo do video* @type duração {String} tempo do video
*/*/
exportexport classclass Videos extends Core { Videos extends Core {
url:url: StringString;;
duracao:duracao: StringString;;
}}
//Galeria//Galeria
importimport { Core } from { Core } from "./core""./core";;
importimport { Fotos } from { Fotos } from "./fotos""./fotos";;
/**/**
* Model de galeria de * Model de galeria de fotosfotos
* @type fotos {Array} lista da model * @type fotos {Array} lista da model de fotosde fotos
*/*/
exportexport classclass Galeria extends Core { Galeria extends Core {
fotos:fotos: ArrayArray<Fotos>;<Fotos>;
}}
//Fotos//Fotos
/**/**
* Model de fotos* Model de fotos
* @type thumb {String} foto principal* @type thumb {String} foto principal
* @type thumbNail {String} foto menor* @type thumbNail {String} foto menor
* @type credito {String} quem tirou a * @type credito {String} quem tirou a fotofoto
* @type legenda {String} descrição da foto* @type legenda {String} descrição da foto
*/*/
exportexport classclass Fotos { Fotos {
thumb:thumb: StringString;;
thumbNail:thumbNail: StringString;;
credito:credito: StringString;;
legenda:legenda: StringString;;
}}
//Podcast//Podcast
/* videos ts*//* videos ts*/
/* videos.ts*//* videos.ts*/
importimport { Core } from { Core } from "./core""./core";;
/**/**
* Model de podcast* Model de podcast
* @type url {String} url do * @type url {String} url do podcastpodcast
* @type duração {String} tempo do audio* @type duração {String} tempo do audio
*/*/
exportexport classclass Podcast extends Core { Podcast extends Core {
url:url: StringString;;
duracao:duracao: StringString;;
}}
No diretório RepositoryNo diretório Repository
No diretórioNo diretório RepositoryRepository , nós mapeamos as nossas, nós mapeamos as nossas modelsmodels com ascom as
nossas collections do nossas collections do MongoDB. AnMongoDB. Analisando esses arquivos, alisando esses arquivos, não hánão há
a necessidade de documentá-los por serem arquivos a necessidade de documentá-los por serem arquivos dede
mapeamento entre as models e as nossas collections. Mas, paramapeamento entre as models e as nossas collections. Mas, parareforçar o nosso conhecimento sobre essa etapa, vamos abrir umreforçar o nosso conhecimento sobre essa etapa, vamos abrir um
dos arquivos e entendê-lo melhor.dos arquivos e entendê-lo melhor.
importimport mongoose from mongoose from "mongoose""mongoose";;
importimport { News } from { News } from "../models/news""../models/news";;
constconst NewsSchema = NewsSchema = newnew mongoose.Schemmongoose.Schema<News>({a<News>({
titulo: { type:titulo: { type: StringString }, },
chapeu: { type:chapeu: { type: StringString }, },
texto: { type:texto: { type: StringString }, },
autor: { type:autor: { type: StringString }, },
imagem: { type:imagem: { type: StringString }, },
dataPublicacaodataPublicacao: { : { type:type: DateDate }, },
tags: { type:tags: { type: StringString }, },
link: { type:link: { type: StringString }, },
ativo: { type:ativo: { type: BooleanBoolean } }
});});
exportexport constconst NewsRepository= NewsRepository = mongoose.modelmongoose.model<News>(<News>("news""news", NewsSchema);, NewsSchema);
Analisando o a Analisando o arquivorquivo NewsSchemaNewsSchema nós temos:nós temos:
E t i t d i í i d iE t i t d i í i d i t dt d
Estamos importando no início do arquivo o Estamos importando no início do arquivo o pacote dopacote do mongoosemongoose
e a classee a classe NewsNews ;;
Em seguida, nós criamos uma constante que está criando umEm seguida, nós criamos uma constante que está criando um
schema deschema de NewsNews , passando todos os atributos que a nossa, passando todos os atributos que a nossa
collection deve ter;collection deve ter;
No final do arquivo, nós No final do arquivo, nós exportamos a constanteexportamos a constante NewsRepositoryNewsRepository ..
ContractsContracts
No diretórioNo diretório contractscontracts , nós criamos as nossas interfaces, ou, como, nós criamos as nossas interfaces, ou, como
aprendemos neste livro, os nossos aprendemos neste livro, os nossos contratos.contratos.
Nesse diretório, nós temos os Nesse diretório, nós temos os seguintes arquivos:seguintes arquivos:
iGaleriaService.tsiGaleriaService.ts ,, iNewsService.tsiNewsService.ts ,, iPodcastService.tsiPodcastService.ts ,,
iVideosService.tsiVideosService.ts ee iService.tsiService.ts , que é uma , que é uma interface genéricainterface genérica
desenvolvida com dois métodos,desenvolvida com dois métodos, getget ee getAllgetAll , que devem ser , que devem ser
implementados pelas outras interfaces.implementados pelas outras interfaces.
A seguir você tem o A seguir você tem o código atualizadcódigo atualizado com o JSDoc de cada uo com o JSDoc de cada umama
dessas interfaces:dessas interfaces:
//iService.ts//iService.ts
importimport { Result } from { Result } from "../infra/result""../infra/result";;
/**/**
* Interface genérica para * Interface genérica para retorno de pesquisasretorno de pesquisas
*/*/
exportexport interfaceinterface IService<T> { IService<T> {
/**/**
* @summary busca por id* @summary busca por id
* @param id {String}* @param id {String}
* @returns retorna o resultado de uma busca pelo * @returns retorna o resultado de uma busca pelo seu idseu id
*/*/
getget(id:(id: stringstring): Promise<T>;): Promise<T>;
/**/**
* @summary Realiza uma busca paginada de uma model* @summary Realiza uma busca paginada de uma model
* @param page {number} pagina* @param page {number} pagina
* @p* @param aram qtd qtd {Number} {Number} quantidade quantidade de de itensitens
* @returns retorna uma lista de T onde T é uma model* @returns retorna uma lista de T onde T é uma model
*/*/
*/*/
getAll(page: getAll(page: numbernumber, qtd:, qtd: numbernumber): ): Promise<ResultPromise<Result<T>>;<T>>;
}}
//iGaleriaService.ts//iGaleriaService.ts
importimport { Galeria } from { Galeria } from "../models/galeria""../models/galeria";;
importimport { IService } from { IService } from "./iService""./iService";;
/**/**
* Contrato * Contrato IGaleriaServiceIGaleriaService
* @summary esse contrato * @summary esse contrato implementa a interface IService passando aimplementa a interface IService passando a
model de Galeriamodel de Galeria
*/*/
exportexport interfaceinterface IGaleriaService extends IService<Galeria> { } IGaleriaService extends IService<Galeria> { }
//iNewsService.ts//iNewsService.ts
importimport { News } from { News } from "../models/news""../models/news";;
importimport { IService } from { IService } from "./iService""./iService";;
/**/**
* Contrato * Contrato INewsServiceINewsService
* @summary esse contrato implementa a interface IService passando a model* @summary esse contrato implementa a interface IService passando a model
de Newsde News
*/*/
exportexport interfaceinterface INewsService extends IService<News> { } INewsService extends IService<News> { }
//iPodcastService.ts//iPodcastService.ts
importimport { Podcast } from { Podcast } from "../models/podcast""../models/podcast";;
importimport { IService } from { IService } from "./iService""./iService";;
/**/**
* * Contrato IPodcastServiceContrato IPodcastService
* @summary esse contrato implementa a interface IService passando a model* @summary esse contrato implementa a interface IService passando a model
de Podcastde Podcast
*/*/
exportexport interfaceinterface IPodcastService extends IService<Podcast> { } IPodcastService extends IService<Podcast> { }
//iVideosService.ts//iVideosService.ts
importimport { Videos } from { Videos } from "../models/videos""../models/videos";;
importimport { IService } from { IService } from "./iService""./iService";;
/**/**
* Contrato * Contrato IVideosServicIVideosServicee
* @summary esse contrato implementa a interface IService passando a model* @summary esse contrato implementa a interface IService passando a model
de Podcastde Podcast
*/*/
exportexport interfaceinterface IVideosService extends IService<Videos> { } IVideosService extends IService<Videos> { }
No diretório ServicesNo diretório Services
No diretórioNo diretório ServicesServices , implementamos os serviços criados dentro do, implementamos os serviços criados dentro do
diretóriodiretório contractscontracts , chamando os nossos, chamando os nossos repositoriesrepositories para criarmospara criarmos
as queriesas queries pesquisas pesquisas dentro do bandentro do banco de dadosco de dados..
Para ficar mais claro, vamos abrir um dos arquivos dentro dessePara ficar mais claro, vamos abrir um dos arquivos dentro desse
diretório e detalhá-lo.diretório e detalhá-lo.
importimport { INewsService } from { INewsService } from "../contracts/iNewsService""../contracts/iNewsService";;
importimport { Result } from { Result } from "../infra/result""../infra/result";;
importimport { News } from { News } from "../models/news""../models/news";;
importimport { NewsRepository } from { NewsRepository } from "../repository/newsRepository""../repository/newsRepository";;
exportexport classclass NewsService NewsService implementsimplements INewsService { INewsService {
asyncasync getget(_id:(_id: stringstring): Promise<News> {): Promise<News> {
letlet result result = await = await NewsRepository.NewsRepository.findById(_id);findById(_id);
returnreturn result; result;
}}
async getAll(page:async getAll(page: numbernumber, qtd:, qtd: numbernumber): Promise<Result<News>> {): Promise<Result<News>> {
letlet result = result = newnew Result<News>()Result<News>();;
result.Page = page;result.Page = page;
result.Qtd = qtd;result.Qtd = qtd;
result.Total = result.Total = await NewsRepository.count({});await NewsRepository.count({});
result.Data = await result.Data = await NewsRepositoryNewsRepository.find({}).skip.find({}).skip((page * ((page * qtd) -qtd) -
qtd).limit(qtd);qtd).limit(qtd);
returnreturn result; result;
}}
}}
No início do arquivo, estamos No início do arquivo, estamos importando os pacotesimportando os pacotes
necessários para a criação do nosso serviço;necessários para a criação do nosso serviço;
Em seguida, nós implementamos os métodos Em seguida, nós implementamos os métodos do nosso contratodo nosso contrato
INewsService INewsService ..
Nós não precisamos documentar esses serviços. Como elesNós não precisamos documentar esses serviços. Como eles
implementam os nossos contratos, nós cimplementam os nossos contratos, nós conseguimos pegar aonseguimos pegar adescrição de cada um dos métodos apenas colocando o mousedescrição de cada um dos métodos apenas colocando o mouse
sobre um dos métodos,sobre um dos métodos, getget ouou getAllgetAll ..
Figura 14.1: NewsService - JSDoc.Figura 14.1: NewsService - JSDoc.
ControllersControllers
As nossas controllers são As nossas controllers são responsáveis por recebresponsáveis por receber todas aser todas as
requisições dos nossos usuários, se comunicando com as outrasrequisições dos nossos usuários, se comunicando com as outras
camadas do nosso projeto e retornando o que o usuário precisa. Nacamadas do nossoprojeto e retornando o que o usuário precisa. Na
nossa API, as controllers estão acessando o nosso banco de dadosnossa API, as controllers estão acessando o nosso banco de dados
através dos nossos serviços e retornando os dados conforme aatravés dos nossos serviços e retornando os dados conforme a
requisição do usuário. Mas isso não é uma regra, nós podemos ter requisição do usuário. Mas isso não é uma regra, nós podemos ter
outras controllers com outras responsabilidades, como o upload deoutras controllers com outras responsabilidades, como o upload de
um arquivo.um arquivo.
Pelo mesmo motivo pelo qual Pelo mesmo motivo pelo qual não precisamos documentar osnão precisamos documentar os
nossos repositórios, nós não precisamos documentar as nossos repositórios, nós não precisamos documentar as nossasnossas
controllers: elas implementam outros serviços que já controllers: elas implementam outros serviços que já estãoestão
documentados.documentados.
Figura 14.2: GaleriaController - JSDoc.Figura 14.2: GaleriaController - JSDoc.
Arquivos de inicialização do projetoArquivos de inicialização do projeto
Nós temos dois arquivos de inicialização do nosso projeto, oNós temos dois arquivos de inicialização do nosso projeto, o
startUp.tsstartUp.ts e oe o program.tsprogram.ts . O. O startUp.tsstartUp.ts seria o arquivoseria o arquivo
configurações do nosso projeto. É nele que configurações do nosso projeto. É nele que devemos inicializar odevemos inicializar o
expressexpress , o nosso arquivo de conexão com o banco de , o nosso arquivo de conexão com o banco de dadosdados db.tsdb.ts
e chamar as nossas rotas.e chamar as nossas rotas.
OO program.tsprogram.ts é o primeiro arquivo chamado no é o primeiro arquivo chamado no nosso projeto, elenosso projeto, ele
importa o arquivoimporta o arquivo startUp.tsstartUp.ts e inicializa oe inicializa o expressexpress ..
Como a finalidade desses arquivos é Como a finalidade desses arquivos é importar e inicializar as nossasimportar e inicializar as nossas
outras classes, que estão todas outras classes, que estão todas documentadas, nós não precisamosdocumentadas, nós não precisamos
documentá-los.documentá-los.
Caso você tenha interesse na versão final do nosso projetoCaso você tenha interesse na versão final do nosso projeto
documentado, documentado, ela está no ela está no meu GitHmeu GitHub:ub:
https://github.cohttps://github.com/programadrianom/programadriano/api_mc_/api_mc_livro/tree/versao-filivro/tree/versao-finalnal..
C 15C 15
ConclusãoConclusão
O objetivo deste livro foi O objetivo deste livro foi abordar o que é TypeScript e auxiliar vocêabordar o que é TypeScript e auxiliar você
desde a instalação até a desde a instalação até a criação de um projeto. Ao criação de um projeto. Ao longo do trajeto,longo do trajeto,
os principais pontos pelos quais passamos os principais pontos pelos quais passamos foram: tipagem,foram: tipagem,
programação Orientada a Objetos, interfaces, Generics, programação Orientada a Objetos, interfaces, Generics, decoratorsdecorators
e Injeção de e Injeção de Dependência.Dependência.
E para que você E para que você esteja alinhado(a) com as tecnologias que estãoesteja alinhado(a) com as tecnologias que estão
em alta no mercado, tivemos uma rápida introdução sobre o que é oem alta no mercado, tivemos uma rápida introdução sobre o que é o
Docker no capítulo 10 e, nos capítulos seguintes, nós montamos umDocker no capítulo 10 e, nos capítulos seguintes, nós montamos um
ambiente de desenvolvimento com ele.ambiente de desenvolvimento com ele.
Por fim, no capítulo 14, nós utilizamos o JSDoc para documentar oPor fim, no capítulo 14, nós utilizamos o JSDoc para documentar o
nosso projeto.nosso projeto.
A versão final do A versão final do projeto aqui projeto aqui desenvolvido pode desenvolvido pode ser encontrada noser encontrada no
seguinte lseguinte link no ink no meu GitHmeu GitHub:ub:
https://github.cohttps://github.com/programadrianom/programadriano/api_mc_/api_mc_livro/tree/versao-filivro/tree/versao-finalnal..
15.1 Obrigado15.1 Obrigado
Espero que este livro tenha lhe ensinado bastante coisa e que vocêEspero que este livro tenha lhe ensinado bastante coisa e que você
possa colocar em prática todo o aprendizado no seu dia a dia, sejapossa colocar em prática todo o aprendizado no seu dia a dia, seja
no desenvolvimento front-end utilizando no desenvolvimento front-end utilizando somente TypeScrisomente TypeScript compt com
algum framework, como algum framework, como o Angularo Angular, ou , ou ainda no desenvolvimentoainda no desenvolvimento
back-end.back-end.
Para que possamos ampliar a nossa rede de amigos, a seguir deixoPara que possamos ampliar a nossa rede de amigos, a seguir deixo
o meu e-mail e minhas redes sociais.o meu e-mail e minhas redes sociais.
tadriano.dev@gmail.comtadriano.dev@gmail.com
https://wwwhttps://www.linkedin.com/in/t.linkedin.com/in/tadriano-net/adriano-net/
Eu ficarei muito Eu ficarei muito feliz de receber o feliz de receber o seu GitHseu GitH ub com os exemplosub com os exemplos
deste livro ou com algum outro exemplo que você tenhadeste livro ou com algum outro exemplo que você tenha
desenvolvido utilizando TypeScript.desenvolvido utilizando TypeScript.