Você provavelmente já ouviu falar do TypeScript, a linguagem criada e mantida pela Microsoft que teve um grande impacto na Internet, com muitas tarefas pendentes adotando e migrando seu código para o TypeScript. É um superconjunto tipado de JavaScript. Em outras palavras, ele fornece variações para JavaScript. No entanto, por que você precisaria dessas variedades? Que vantagens eles criam? E você deve reescrever sua base de código completa para colher os benefícios deles? Essas perguntas e outras serão respondidas neste tutorial do TypeScript para alunos.
Neste artigo, você vai descobrir como o TypeScript pode reduzir erros no código e oferecer mais segurança, tornando o desenvolvimento mais eficiente. Mesmo que você já conheça JavaScript, este guia irá te acompanhar de forma simples, mostrando como o TypeScript pode melhorar sua experiência de programação.
Prepare-se para aprender algo surpreendente e elevar seu nível no desenvolvimento!
Algum código JavaScript impreciso
Para começar, vamos dar uma olhada em algum código JavaScript simples bastante comum que você pode encontrar em qualquer base de código. Ele busca algumas fotos da API do Pexels e as insere no DOM.
No entanto, este código tem vários erros de digitação que vão desencadear problemas. Veja se você pode identificá-los:
const PEXELS_API_KEY = '...';
async function fetchImages(searchTerm, perPage) {
const endResult = await fetch(`https://api.pexels.com/v1/search?question=${searchTerm}&per_page=${perPage}`, {
headers: {
Authorization: PEXELS_API_KEY,
}
});
const information = await endResult.json();
const imagesContainer = document.querySelector('#images-container');
for (const picture of information.pictures) {
const img = doc.createElement('img');
img.src = picture.src.medium;
imagesContainer.append(img);
}
}
fetchImages('canines', 5);
fetchImages(5, 'cats');
fetchImages('puppies');
Você pode ver os problemas dentro da instância acima? Na verdade, caso você execute esse código em um navegador, você obterá erros instantaneamente; no entanto, ao aproveitar ao máximo o TypeScript, obteremos os erros mais rapidamente, fazendo com que o TypeScript localize esses pontos em nosso editor.
Encurtar esse ciclo de sugestões é eficaz – e será mais útil à medida que a escala do seu desafio aumenta. É simples identificar erros nesses 30 traços de código, mas e se você estiver trabalhando em uma base de código com 1000 traços? Você veria algum ponto em potencial simplesmente então?
Observação: não é necessário obter uma chave de API do Pexels para acompanhar este tutorial do TypeScript. No entanto, se você preferir executar o código, um segredo de API é totalmente gratuito: basta ingressar em uma conta e, em seguida, criar uma.
Trabalhando TypeScript do Editor
Antigamente, o TypeScript exigia que cada informação fosse escrita como .ts
informação. No entanto, hoje em dia, a rampa de integração é mais suave. Você não quer que um arquivo TypeScript seja escrito em código TypeScript: como substituto, executaremos TypeScript em qualquer arquivo JavaScript que desejarmos!
Quando você é um usuário do VS Code (não entre em pânico caso não seja – nós iremos até você!), isso pode funcionar no campo sem mais necessidades. Permitiremos a verificação do TypeScript incluindo isso no topo do nosso arquivo JavaScript (é vital que seja a linha principal):
Você deve então obter alguns erros carmesim ondulados em seu editor que destacam nossos erros, conforme ilustrado abaixo.
Você também precisa ver uma cruz no canto esquerdo traseiro com um dois ao lado. Clicar nele pode revelar os problemas que foram percebidos.
E só porque você não está no VS Code não significa que você não poderá obter a mesma experiência com os erros de destaque do TypeScript. A maioria dos editores hoje usa o Language Server Protocol (também conhecido como LSP), que é o que o VS Code usa para alimentar sua integração com o TypeScript.
Vale a pena pesquisar on-line para encontrar seu editor e os plug-ins úteis para organizá-lo.
Colocando e trabalhando o TypeScript regionalmente
Quando você não está no VS Code, caso contrário, gostaria de uma resposta normal, você pode até executar o TypeScript na linha de comando. Nesta parte, apresentarei caminhos.
Primeiro, vamos gerar um novo desafio. Esta etapa pressupõe que você tenha o Node e o npm instalados em sua máquina :
mkdir typescript-demo
cd typescript demo
npm init -y
Em seguida, adicione TypeScript ao seu desafio:
npm set up --save-dev typescript
Observação: você pode configurar o TypeScript globalmente em sua máquina, mas eu gosto de instalá-lo por projeto. Dessa forma, garanto que controlei exatamente qual modelo de TypeScript cada desafio usa. Isso é útil, você provavelmente tem um desafio que não toca há algum tempo; você pode continuar usando um modelo TS mais antigo nesse desafio, enquanto tem um desafio mais moderado usando um modelo mais moderno.
Assim que for inserido, você poderá executar o compilador TypeScript ( tsc
) para obter os mesmos erros (não se preocupe com esses outros sinalizadores, pois falaremos mais sobre eles em breve):
npx tsc index.js --allowJs --noEmit --target es2015
index.js:13:36 - error TS2551: Property 'qerySelector' doesn't exist on sort 'Doc'. Did you imply 'querySelector'?
13 const imagesContainer = document.querySelector('#images-container');
~~~~~~~~~~~~
node_modules/typescript/lib/lib.dom.d.ts:11261:5
11261 querySelector<Okay extends keyof HTMLElementTagNameMap>(selectors: Okay): HTMLElementTagNameMap[K] | null;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'querySelector' is asserted right here.
index.js:16:9 - error TS2339: Property 'src' doesn't exist on sort 'HTMLElement'.
16 img.src = picture.src.medium;
~~~
Discovered 2 errors.
Você poderá ver que o TypeScript na linha de comando destaca os erros de código JavaScript idênticos que o VS Code destacou na captura de tela acima.
Corrigindo os erros em nosso código JavaScript
Agora que temos o TypeScript instalado e funcionando, vamos dar uma olhada em como veremos e depois corrigir os erros que o TypeScript está sinalizando.
Vamos verificar nosso primeiro erro.
A propriedade qerySelector
não existe na classificaçãoDoc
index.js:13:36 - error TS2551: Property 'qerySelector' doesn't exist on sort 'Doc'. Did you imply 'querySelector'?
13 const imagesContainer = document.querySelector('#images-container');
node_modules/typescript/lib/lib.dom.d.ts:11261:5
11261 querySelector<Okay extends keyof HTMLElementTagNameMap>(selectors: Okay): HTMLElementTagNameMap[K] | null;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'querySelector' is asserted right here.
Isso pode parecer bastante complicado caso você não esteja acostumado a estudar os erros do TypeScript, portanto, não entre em pânico se parecer um pouco estranho! O TypeScript notou que, on-line 13
, nos referimos como uma técnica document.querySelector. Queríamos dizer, doc.querySelector
no entanto, cometeu um erro ao digitar. Teríamos descoberto isso quando tentamos executar nosso código no navegador, mas o TypeScript está pronto para nos alertar sobre isso mais cedo.
Na metade seguinte, ele destaca lib.dom.d.ts
e a querySelector<Okay...>
operação está mergulhando no código TypeScript mais avançado, então não se preocupe com isso, mas, em um nível alto, é o TypeScript nos mostrando que sabe que existe um método chamado , querySelector
e suspeita nós precisaríamos disso.
Vamos agora ampliar a parte final da mensagem de erro acima:
index.js:13:36 - error TS2551: Property 'qerySelector' doesn't exist on sort 'Doc'. Did you imply 'querySelector'?
Particularmente, preciso dar uma olhada no conteúdo textual didn't exist on sort 'Doc'
. No TypeScript (e amplamente em cada linguagem tipada), os objetos têm o que é chamado de arquivo sort
.
No TypeScript, números como 1
ou 2.5
têm o tipo quantity
, strings como "howdy world"
têm o tipo string
e uma ocasião de um componente HTML tem o tipo HTMLElement
. Isso é o que permite ao compilador do TypeScript testar se nosso código está correto. Uma vez que ele está ciente do tipo de uma coisa, ele está ciente de quais recursos você pode nomear que levam aquela coisa, ou quais estratégias existem nela.
Observação: se você quiser aprender mais sobre variedades de informações, consulte o guia “ Introdução às classificações de informações: estáticas, dinâmicas, robustas e fracas ”.
Em nosso código, o TypeScript viu que nos referimos a doc
. Isso pode ser uma variável mundial dentro do navegador, e o TypeScript está ciente disso e está ciente de que tem o tipo de arquivo Doc
. Essa papelada gentil (no caso de você perdoar o trocadilho!) Todas as estratégias que citaremos. Esta é a razão pela qual o TypeScript sabe que querySelector
é uma técnica e que o erro ortográfico qerySelector
não é.
Veremos mais desses tipos conforme passamos por outros tutoriais do TypeScript , mas é daí que vem todo o poder do TypeScript. Descreveremos rapidamente nossas variedades pessoais, o que significa que, na verdade, prolongaremos o sistema de classificação para obter informações sobre todo o nosso código e o que faremos ou não com qualquer objeto explícito em nossa base de código.
Agora vamos voltar nossa atenção para nosso erro subseqüente, que é um pouco menos claro.
A propriedade src
não existe na classificaçãoHTMLElement
index.js:16:9 - error TS2339: Property 'src' doesn't exist on sort 'HTMLElement'.
16 img.src = picture.src.medium;
Esse é um tipo de erro onde geralmente é necessário olhar um pouco acima do erro para encontrar o problema. Todos nós sabemos que um fator de imagem HTML tem um src
atributo, então por que o TypeScript não tem?
const img = doc.createElement('img');
img.src = picture.src.medium;
O erro aqui está na primeira linha: depois de criar um novo fator de imagem, é necessário nomeá-lo doc.createElement('img')
(porque a tag HTML é <img>
, não <picture>
). Assim que tentamos isso, o erro desaparece, porque o TypeScript está ciente disso, uma vez que você nomeia doc.createElement('img')
, você obtém novamente um componente que possui uma src
propriedade. E isso é tudo até as variedades .
Enquanto você nomeia doc.createElement('div')
, a coisa retornada é do tipo HTMLDivElement
. Enquanto você nomeia doc.createElement('img')
, a coisa retornada é semelhante HTMLImageElement
. HTMLImageElement
tem uma src
propriedade declarada nele, então o TypeScript está ciente de que você poderá nomear img.src
. No entanto HTMLDivElement
, não, então o TypeScript apresentará erros.
No caso de doc.createElement('img')
, porque o TypeScript não descobre nenhum elemento HTML com a tag picture
, ele retornaria um objeto de classificação HTMLElement
(um elemento HTML genérico, não específico de pelo menos uma tag), que também não possui a src
propriedade .
Assim que repararmos esses dois erros e executarmos novamente o TypeScript, você verá que não obtivemos nada novamente, o que mostra que não houve erros. Quando você configurou seu editor para indicar erros, esperamos que nenhum seja exibido.
Como você pode configurar o TypeScript
É um pouco trabalhoso adicionar // @ts-check
a cada arquivo, e quando executamos o comando no terminal para adicionar esses outros sinalizadores. O TypeScript permite que você, em vez disso, o permita em um desafio de JavaScript criando um jsconfig.json
arquivo.
Crie jsconfig.json
dentro da listagem raiz do nosso desafio e coloque isso dentro dela:
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"goal": "es2015"
},
"embrace": ["*.js"]
}
Isso configura o compilador TypeScript (e a integração TS do seu editor) para:
- Examine as informações do JavaScript (a
checkJs
possibilidade). - Suponha que estamos construindo em uma atmosfera ES2015 (a
goal
possibilidade). Padronizar para ES2015 significa que usaremos problemas como garantias sem que o TypeScript nos dê erros. - Não produza nenhuma informação compilada (a
noEmit
possibilidade). Enquanto estiver escrevendo o código TypeScript nas informações de fornecimento do TypeScript, você deseja que o compilador gere o código JavaScript para que você possa executá-lo no navegador. Como estamos escrevendo o código JavaScript que opera no navegador, não queremos que o compilador gere nenhuma informação para nós. - Por fim,
embrace: ["*.js"]
instrui o TypeScript a examinar qualquer arquivo JavaScript na listagem raiz.
Agora que temos este arquivo, você poderá substituir sua instrução de linha de comando por esta:
npx tsc -p jsconfig.json
Isso pode executar o compilador com nosso arquivo de configuração ( -p
aqui é a abreviação de “desafio”), para que você não precise cruzar todos esses sinalizadores ao operar o TypeScript.
Trabalhando em modo estrito
Agora que estamos aqui, vamos ver como tornaremos o TypeScript muito mais completo ao verificar nosso código. O TypeScript suporta algo chamado “modo estrito”, que instrui o TypeScript a testar nosso código mais completamente e garantir que lidamos com quaisquer possíveis ocasiões em que, por exemplo, um objeto pode ser undefined
. Para tornar isso mais claro, vamos ligá-lo e ver quais erros obtemos. Adicione "strict": true
a "compilerOptions"
uma parte de e jsconfig.json
, em seguida, execute novamente o TypeScript na linha de comando.
Enquanto você faz uma alteração no jsconfig.json
arquivo, pode descobrir que precisa reiniciar o editor para que ele selecione essas modificações. Portanto, caso você não esteja vendo os mesmos erros que eu, experimente.
npx tsc -p jsconfig.json
index.js:3:28 - error TS7006: Parameter 'searchTerm' implicitly has an 'any' sort.
3 async function fetchImages(searchTerm, perPage) {
~~~~~~~~~~
index.js:3:40 - error TS7006: Parameter 'perPage' implicitly has an 'any' sort.
3 async function fetchImages(searchTerm, perPage) {
~~~~~~~
index.js:15:5 - error TS2531: Object is probably 'null'.
15 imagesContainer.append(img);
~~~~~~~~~~~~~~~
Discovered 3 errors.
Vamos começar com o erro final primeiro e estão disponíveis novamente para os outros:
index.js:15:5 - error TS2531: Object is probably 'null'.
15 imagesContainer.append(img);
~~~~~~~~~~~~~~~
E vamos dar uma olhada em como imagesContainer
é delineado:
const imagesContainer = doc.querySelector('#images-container');
Ativar o strict
modo tornou o TypeScript mais rigoroso em garantir que os valores com os quais contamos existam. Neste caso, não é garantido que doc.querySelector('#images-container')
realmente retornará um componente; e se não for descoberto? doc.querySelector
retornará null
se um componente não for descoberto e agora habilitamos o modo estrito, o TypeScript está nos dizendo que imagesContainer
pode ser realmente null
.
Classificações de União
Antes de ativar o modo estrito, o tipo imagesContainer
era Component
, mas agora ativamos o modo estrito, o tipo imagesContainer
é Component | null
. O |
operador (pipe) cria variedades de união – que você aprenderá como “ou” – então aqui imagesContainer
é de tipo Component
ou null
. Quando o TypeScript nos diz Object is probably 'null'
, é exatamente isso que está nos dizendo e quer que tenhamos certeza de que a coisa existe antes de usá-la.
Vamos corrigir isso lançando um erro caso não descubramos o fator de contêiner de fotos:
const imagesContainer = doc.querySelector('#images-container');
if (imagesContainer === null) {
throw new Error('Couldn't discover images-container factor.')
}
for (const picture of information.pictures) {
const img = doc.createElement('img');
img.src = picture.src.medium;
imagesContainer.append(img);
}
O TypeScript agora está satisfeito; lidamos com o null
caso lançando um erro. O TypeScript é sábio o suficiente para entender agora que, se nosso código não gerar um erro na terceira linha do trecho acima, imagesContainer
não é null
, e devido a esse fato deve existir e deve ser do tipo Component
.
Seu tipo era Component | null
, mas quando era, null
poderíamos ter gerado um erro, então agora deveria ser Component
. Esse desempenho é chamado de redução de classificação e é uma ideia realmente útil para se prestar atenção.
Implícito qualquer
Agora vamos mudar nossa consideração para os dois erros restantes que temos:
index.js:3:28 - error TS7006: Parameter 'searchTerm' implicitly has an 'any' sort.
3 async function fetchImages(searchTerm, perPage) {
~~~~~~~~~~
index.js:3:40 - error TS7006: Parameter 'perPage' implicitly has an 'any' sort.
3 async function fetchImages(searchTerm, perPage) {
Uma das muitas implicações de ativar o modo estrito é que ele ativa uma regra conhecida como noImplicitAny
. Por padrão, quando o TypeScript não conhece o tipo de uma coisa, o padrão é fornecer uma classificação específica do TypeScript chamada any
. any
não é um tipo bom de se ter em seu código, porque não há nenhum guia relacionado a ele por meio do que o compilador testará. Vai permitir que algo ocorra.
Prefiro imaginá-lo porque o compilador levanta os braços no ar e diz “Não posso ajudar aqui!” A utilização any
desativa qualquer verificação de classificação útil para essa variável individual, por isso recomendo fortemente evitá-la.
Descrever a assinatura de operação com JSDoc
Os 2 erros acima são TypeScript nos dizendo que não informamos quais são as variedades das 2 variáveis que nossa operação usa e que as está padronizando novamente para any
. A boa notícia é que fornecer essas informações ao TypeScript significava reescrever seu arquivo no código TypeScript ;
Por exemplo, aqui está como apresentaremos as informações de classificação para nossa fetchImages
operação:
async function fetchImages(searchTerm, perPage) {
}
Todos os comentários JSDoc devem começar com /**
(palavra o adicional *
no início) e dentro deles usamos tags específicas, começando com @
, para indicar as propriedades de classificação. Aqui declaramos dois parâmetros ( @param
), e depois colocamos sua classificação entre chaves (idênticos aos objetos JavaScript comuns).
Aqui deixamos claro que searchTerm
é a string
e perPage
é uma quantidade. Enquanto estamos nisso, também usamos @return
para declarar o que essa operação retorna. No nosso caso, não retorna nada, e a classificação que usamos no TypeScript para declarar isso é void
.
Vamos agora executar novamente o compilador e ver o que ele diz:
npx tsc -p jsconfig.json
index.js:30:13 - error TS2345: Argument of sort 'quantity' isn't assignable to parameter of sort 'string'.
30 fetchImages(5, 'cats')
~
index.js:31:1 - error TS2554: Anticipated 2 arguments, however obtained 1.
31 fetchImages('puppies')
~~~~~~~~~~~~~~~~~~~~~~
index.js:9:40
9 async function fetchImages(searchTerm, perPage) {
~~~~~~~
An argument for 'perPage' was not supplied.
Discovered 2 errors.
Essa é a coisa fantástica sobre o TypeScript. Dando mais informações ao compilador, ele provavelmente agora detectará erros em como estamos chamando o código que não poderia antes. Neste caso, são descobertas duas chamadas para fetchImages
o local onde obtivemos os argumentos dentro da ordem falaciosa, e a segunda para o local onde esquecemos o perPage
argumento (nenhum dos dois searchTerm
, perPage
são parâmetros eletivos).
Vamos simplesmente excluir essas chamadas, no entanto, espero que ajude a mostrar a facilidade do compilador e as vantagens de fornecer ao compilador mais informações de classificação.
Declarando classificações de informações usando uma interface
Embora não seja sinalizado pelo compilador, uma preocupação que nosso código tem está nesta linha:
const information = await endResult.json();
O problema aqui é que o tipo de retorno await endResult.json()
é any
. É porque, uma vez que você pega uma resposta da API e a converte em JSON, o TypeScript não tem noção de quais informações estão lá, então o padrão é any
. No entanto, como todos sabemos o que a API Pexels retorna, podemos fornecer algumas informações de classificação por meio do uso de interfaces TypeScript . Isso nos permite informar o TypeScript em relação à forma de um objeto: quais propriedades ele possui e quais valores essas propriedades possuem.
Vamos declarar uma interface — mais uma vez, utilizando a sintaxe JSDoc, que representa as informações retornadas da API do Pexels. Usei a referência da API Pexels para determinar quais informações são retornadas. Neste caso, vamos realmente delinear duas interfaces: uma irá declarar a forma de um single picture
que a API Pexels retorna, e a outra irá declarar a forma geral da resposta da API.
Para delinear essas interfaces usando JSDoc, usamos @typedef
, que nos permite declarar variedades mais avançadas. Em seguida, usamos @property
para declarar propriedades únicas nessa interface. Por exemplo, aqui está o tipo que eu crio para uma pessoa Picture
. As classificações devem sempre começar com uma letra maiúscula.
Se você preferir ver uma referência completa de todo o desempenho JSDoc suportado, o site do TypeScript tem uma lista completa com exemplos .
Esse tipo diz que qualquer objeto digitado como a Picture
pode ter uma propriedade, src
, que é um objeto com três propriedades de string: medium
, massive
e thumbnail
. Você descobrirá que a API Pexels retorna mais; você não precisa declarar cada propriedade que um objeto possui caso não precise, mas apenas o subconjunto que deseja. Aqui, nosso aplicativo atualmente usa apenas a medium
imagem, mas declarei alguns outros tamanhos que precisaríamos mais cedo ou mais tarde.
Agora que temos esse sort, vamos declarar o sort PexelsSearchResponse
, que pode caracterizar o que obtemos novamente da API:
Esse é o lugar onde você poderá ver o valor de declarar suas variedades pessoais; nós declaramos que este objeto tem uma propriedade, pictures
e então declaramos que seu valor é uma matriz, onde cada mercadoria é desse tipo Picture
. Isso é o que a Array<X>
sintaxe denota: é uma matriz onde cada mercadoria dentro da matriz é de tipo X
. [1, 2, 3]
poderia ser um Array<quantity>
, por exemplo.
Assim que conseguirmos isso, usaremos a @sort
observação JSDoc para informar ao TypeScript que as informações que obtemos novamente endResult.json()
são do tipo PexelsSearchResponse
:
const information = await endResult.json();
@sort
não é algo que você deva obter regularmente. Normalmente, você precisa que o compilador resolva de forma inteligente o tipo de problema, razoavelmente do que deve informá-lo sem rodeios. Mas, como resultado de endResult.json()
return any
, podemos substituir isso por nossa classificação.
Dê uma olhada se todo o lote está funcionando
Para mostrar que isso está funcionando, escrevi errado intencionalmente medium
ao referenciar o URL da imagem:
for (const picture of information.pictures) {
const img = doc.createElement('img');
img.src = picture.src.mediun;
imagesContainer.append(img);
}
Se executarmos o TypeScript mais uma vez, veremos o problema que o TypeScript não teria notado se não tivéssemos conseguido o trabalho que simplesmente fizemos para declarar a interface:
index.js:35:25 - error TS2551: Property 'mediun' doesn't exist on sort '{ medium: string; massive: string; thumbnail: string; }'. Did you imply 'medium'?
35 img.src = picture.src.mediun;
~~~~~~
index.js:18:18
18 * @property {{medium: string, massive: string, thumbnail: string}} src
~~~~~~
'medium' is asserted right here.
Discovered 1 error.