Arte - TypeScript.pdf
C
M
Y
CM
MY
CY
CMY
K
1
26/01/17
16:07
EDIÇÃO FCA – Editora de Informática, Lda. Av. Praia da Vitória, 14 A – 1000-247 Lisboa Tel: +351 213 511 448 fca@fca.pt www.fca.pt DISTRIBUIÇÃO Lidel – Edições Técnicas, Lda. Rua D. Estefânia, 183, R/C Dto. – 1049-057 Lisboa Tel: +351 213 511 448 lidel@lidel.pt www.lidel.pt LIVRARIA Av. Praia da Vitória, 14 A – 1000-247 Lisboa Tel: +351 213 511 448 * Fax: +351 213 522 684 livraria@lidel.pt Copyright © 2017, FCA – Editora de Informática, Lda. ISBN edição impressa: 978-972-722-864-5 1.ª edição impressa: fevereiro 2017 Impressão e acabamento: Tipografia Lousanense, Lda – Lousã Depósito Legal n.º 420717/17 Capa: José M. Ferrão – Look-Ahead
Marcas Registadas de FCA – Editora de Informática, Lda. –
®
®
®
Todos os nossos livros passam por um rigoroso controlo de qualidade, no entanto aconselhamos a consulta periódica do nosso site (www.fca.pt) para fazer o download de eventuais correções. Não nos responsabilizamos por desatualizações das hiperligações presentes nesta obra, que foram verificadas à data de publicação da mesma. Os nomes comerciais referenciados neste livro têm patente registada. Reservados todos os direitos. Esta publicação não pode ser reproduzida, nem transmitida, no todo ou em parte, por qualquer processo eletrónico, mecânico, fotocópia, digitalização, gravação, sistema de armazenamento e disponibilização de informação, sítio Web, blogue ou outros, sem prévia autorização escrita da Editora, exceto o permitido pelo CDADC, em termos de cópia privada pela AGECOP – Associação para a Gestão da Cópia Privada, através do pagamento das respetivas taxas.
ÍNDICE GERAL COMO UTILIZAR ESTE LIVRO..................................................................................IX O que posso encontrar neste livro?........................................................................................................ IX Requisitos .................................................................................................................................................. IX A quem se dirige este livro? .....................................................................................................................X Convenções .................................................................................................................................................X Organização do livro .................................................................................................................................X Capítulo 1 – Introdução ................................................................................................................... XI Capítulo 2 – Tipos básicos ............................................................................................................... XI Capítulo 3 – Funções ........................................................................................................................ XI Capítulo 4 – Interfaces ..................................................................................................................... XI Capítulo 5 – Classes ......................................................................................................................... XI Capítulo 6 – Outros Tipos .............................................................................................................. XII Capítulo 7 – Módulos...................................................................................................................... XII Capítulo 8 – Projeto: Gestão de Contactos ................................................................................... XII Suporte ..................................................................................................................................................... XII 1. INTRODUÇÃO ................................................................................................... 1 1.1. Porquê utilizar TypeScript? ............................................................................................................... 1 1.2. Instalação ............................................................................................................................................. 2 1.3. Editor Visual Studio Code .................................................................................................................... 4 1.3.1. Compilação do código ............................................................................................................. 6 1.3.2. Configuração de projetos TypeScript .................................................................................. 11 1.3.3. Debbuging ................................................................................................................................. 13 Resumo ...................................................................................................................................................... 17 2. TIPOS BÁSICOS ............................................................................................. 19 2.1. Variáveis............................................................................................................................................. 19 2.2. Tipos básicos ...................................................................................................................................... 21 2.3. Utilização de tipos ............................................................................................................................ 23 2.3.1. Inferência de tipos .................................................................................................................. 25 2.4. Enums ................................................................................................................................................. 27 2.5. Arrays e Tuples ................................................................................................................................... 29 2.6. Null e Undefined ................................................................................................................................. 31 2.7. Never ................................................................................................................................................... 33 2.8. Conversão explícita de tipos............................................................................................................ 34 Resumo ...................................................................................................................................................... 35 3. FUNÇÕES ...................................................................................................... 37 3.1. Introdução .......................................................................................................................................... 37 3.2. Especificação de tipos ....................................................................................................................... 37
© FCA – Editora de Informática
VI
TYPESCRIPT – O JAVASCRIPT MODERNO PARA CRIAÇÃO DE APLICAÇÕES
3.3. Funções como tipos .......................................................................................................................... 38 3.3.1. Inferência de tipos .................................................................................................................. 40 3.4. Parâmetros ......................................................................................................................................... 41 3.4.1. Parâmetros opcionais ............................................................................................................. 41 3.4.2. Parâmetros com valores predefinidos ................................................................................. 43 3.4.3. Parâmetros rest ........................................................................................................................ 45 3.5. Funções arrow .................................................................................................................................... 45 3.5.1. Termo this ................................................................................................................................ 46 3.6. Overload de funções........................................................................................................................... 48 Resumo ...................................................................................................................................................... 50 4. INTERFACES .................................................................................................. 51 4.1. Introdução .......................................................................................................................................... 51 4.1.1. Forma vs. nome ....................................................................................................................... 51 4.2. Sintaxe ................................................................................................................................................ 52 4.2.1. Membros opcionais ................................................................................................................ 54 4.2.2. Membros de leitura ................................................................................................................ 56 4.2.3. Extensão de interfaces ............................................................................................................ 57 4.3. Compatibilidade com interfaces ..................................................................................................... 57 4.3.1. Objetos literais......................................................................................................................... 58 4.3.2. Funções .................................................................................................................................... 59 4.3.3. Classes ...................................................................................................................................... 60 4.3.4. Índices ...................................................................................................................................... 62 4.3.5. Tipos híbridos ......................................................................................................................... 63 Resumo ...................................................................................................................................................... 65 5. CLASSES....................................................................................................... 67 5.1. Introdução .......................................................................................................................................... 67 5.2. Visibilidade ........................................................................................................................................ 68 5.3. Parâmetros como propriedades ...................................................................................................... 73 5.4. Getters e setters ................................................................................................................................... 74 5.5. Membros opcionais........................................................................................................................... 76 5.6. Membros estáticos ............................................................................................................................ 77 5.7. Herança .............................................................................................................................................. 78 5.7.1. Override de membros.............................................................................................................. 80 5.8. Classes abstratas................................................................................................................................ 80 5.9. Classes usadas como interfaces ....................................................................................................... 82 Resumo ...................................................................................................................................................... 82 6. OUTROS TIPOS .............................................................................................. 83 6.1. Uniões ................................................................................................................................................. 83 6.1.1. Guardas.................................................................................................................................... 85 6.1.1.1. typeof e instanceof .......................................................................................................... 87 6.1.2. Tagged unions ........................................................................................................................... 87 6.2. Interseções.......................................................................................................................................... 91 6.3. Aliases.................................................................................................................................................. 92 © FCA – Editora de Informática
ÍNDICE GERAL
VII
6.4. String literal types ............................................................................................................................... 92 6.5. Símbolos ............................................................................................................................................. 94 6.6. Iterators e geradores .......................................................................................................................... 95 6.6.1. Iterators personalizados.......................................................................................................... 97 6.6.2. Funções geradoras .................................................................................................................. 98 6.7. Mixins ................................................................................................................................................. 99 6.8. Genéricos.......................................................................................................................................... 100 6.8.1. Sintaxe .................................................................................................................................... 101 6.9. Promises ............................................................................................................................................ 106 Resumo .................................................................................................................................................... 111 7. MÓDULOS ................................................................................................... 113 7.1. Introdução ........................................................................................................................................ 113 7.2. Sintaxe .............................................................................................................................................. 114 7.2.1. Exportação de vários elementos ......................................................................................... 116 7.2.2. Instrução de exportação ....................................................................................................... 116 7.2.3. Exportações e importações com nomes diferentes ........................................................... 117 7.2.4. Importações totais ................................................................................................................ 118 7.2.5. Importações para execução de código. .............................................................................. 118 7.2.6. Exportações de importações (reexportações) .................................................................... 118 7.2.7. Exportações predefinidas .................................................................................................... 120 7.3. Geração de código........................................................................................................................... 121 7.3.1. AMD ....................................................................................................................................... 121 7.3.2. CommonJS............................................................................................................................... 122 7.3.3. Observações finais ................................................................................................................ 123 7.4. Recomendações ............................................................................................................................... 126 Resumo .................................................................................................................................................... 126 8. PROJETO: GESTÃO DE CONTACTOS .................................................................... 127 8.1. Introdução ........................................................................................................................................ 127 8.2. Preparação ....................................................................................................................................... 128 8.2.1. Ficheiro tsconfig.json ............................................................................................................. 132 8.2.2. Typings ................................................................................................................................... 133 8.2.3. SystemJS ................................................................................................................................. 135 8.2.4. Página HTML ........................................................................................................................ 136 8.3. Alunos e Contactos ......................................................................................................................... 138 8.4. Knockout ............................................................................................................................................ 141 8.5. Vista .................................................................................................................................................. 147 8.6. Bootstrapping .................................................................................................................................... 149 8.7. Observações finais .......................................................................................................................... 150 Resumo .................................................................................................................................................... 151 ÍNDICE REMISSIVO ........................................................................................... 153
© FCA – Editora de Informática
COMO UTILIZAR ESTE LIVRO Nos últimos anos, o JavaScript impôs-se como linguagem dominante da Web. A flexibilidade inerente à inexistência de verificação da tipificação caraterística desta linguagem acaba por introduzir alguns problemas, especialmente perante aplicações com alguma dimensão. Foi a pensar nestes cenários que a Microsoft desenvolveu o TypeScript, uma nova linguagem open source, que pode ser vista como um superset de JavaScript, em que a tipificação de valores assume um papel fulcral na redução de erros associados à produção de código JavaScript.
O QUE POSSO ENCONTRAR NESTE LIVRO? Este livro centra-se na apresentação das principais caraterísticas e funcionalidades disponibilizadas pela linguagem TypeScript. Para além de introduzir algumas das vantagens associadas ao uso desta linguagem, o livro detalha as principais caraterísticas associadas à escrita de código nesta linguagem, comparando-as, sempre que necessário, com as disponibilizadas pela linguagem JavaScript. Assim, o livro começa por apresentar algumas das vantagens associadas ao uso da linguagem para, em seguida, se debruçar sobre a instalação das ferramentas que lhes servem de suporte (incluindo o editor escolhido para escrever o código dos exemplos apresentados no livro – neste caso, optou-se por utilizar o VS Code). Para além disso, o leitor poderá encontrar informação sobre os tipos de dados básicos e ver como estes podem ser utilizados na criação de novos tipos personalizados. Veremos também como a introdução de determinados tipos de dados inexistentes em JavaScript (ex.: interfaces) podem contribuir para aumentar a nossa produtividade como programadores. Finalmente, existirá ainda tempo para vermos como a linguagem introduz algumas adaptações que contribuem para melhorar várias das novidades introduzidas pelo ECMAScript 2015 (ex.: melhorias ao nível da definição de classes). No final deste livro, e se tivermos feito um bom trabalho, será fácil concluir que o TypeScript é, de facto, uma excelente linguagem, que desempenha um papel importante no aumento da produtividade inerente à escrita de código JavaScript.
REQUISITOS Este é um livro prático; no entanto, foi escrito para permitir que a aprendizagem das funcionalidades da linguagem possa ser feita sem que o leitor tenha de estar sentado junto a um computador. Apesar de todos os exemplos terem sido escritos sobre sistemas Windows, a verdade é que todo o código apresentado pode ser executado sem qualquer problema noutros sistemas operativos (como, por exemplo, OS X ou Linux). Na prática, se a plataforma em questão suportar o Node.js, então deverá também suportar o TypeScript © FCA – Editora de Informática
X
TYPESCRIPT – O JAVASCRIPT MODERNO PARA CRIAÇÃO DE APLICAÇÕES
(como veremos no Capítulo 1, o TypeScript pode ser instalado como utilitário num ambiente Node.js). No que diz respeito ao editor utilizado na escrita de código, optámos pelo Visual Studio Code (VS Code). No Capítulo 1 apresentaremos mais detalhes sobre a instalação e a utilização deste editor que, para além de poder ser obtido gratuitamente (em https://www.visualstudio.com/en-us/products/code-vs.aspx), pode ser instalado nos sistemas operativos mais utilizados (Windows, OS X e Linux).
A QUEM SE DIRIGE ESTE LIVRO? Este livro é dirigido a todos aqueles que já possuem alguns conhecimentos em JavaScript e que pretendem colocar-se rapidamente a par das principais funcionalidades introduzidas pelo TypeScript. Se o leitor procura um livro que se centre na linguagem JavaScript ou na plataforma Node.js, então ficará, com toda a certeza, mais bem servido com um dos seguintes livros: JavaScript, JavaScript 6 ou Node.js – Construção de Aplicações Web, todos publicados pela FCA.
CONVENÇÕES Ao longo deste livro, optou-se por seguir um conjunto de convenções que facilitam a interpretação do texto e do código apresentados. Assim, todos os excertos de código são apresentados no seguinte formato: let a
=10;
O caráter é usado para representar instruções que tiveram de ser divididas em várias linhas por falta de espaço no livro. Por sua vez, as notas ou observações importantes poderão ser encontradas no interior de uma secção semelhante à seguinte: Nota importante Esta é uma nota ou observação importante.
ORGANIZAÇÃO DO LIVRO Este livro encontra-se dividido em oito capítulos, que podem ser lidos sequencialmente ou, se o leitor assim o preferir, alternadamente (isto é, sem respeitar a ordem de capítulos apresentada). A leitura sequencial conduz o leitor ao longo de um processo de aprendizagem da linguagem, que começa por introduzir os conceitos base antes de apresentar alguns aspetos mais avançados que, muito provavelmente, não serão utilizados frequentemente no dia a dia. Por outro lado, a leitura não sequencial dos capítulos permite o uso do livro como obra de referência. Esta aproximação é possível devido ao facto de todas as (poucas) dependências entre capítulos estarem devidamente identificadas. © FCA – Editora de Informática
COMO UTILIZAR ESTE LIVRO
XI
CAPÍTULO 1 – INTRODUÇÃO Este capítulo inicia o nosso estudo do TypeScript. Depois de apresentar algumas das vantagens inerentes ao uso desta linguagem, o capítulo introduz as principais estratégias que podem ser seguidas na instalação das ferramentas que lhe servem de suporte. O capítulo termina com uma breve introdução ao editor VS Code, que será utilizado na escrita de código TypeScript apresentado nos restantes capítulos.
CAPÍTULO 2 – TIPOS BÁSICOS Neste capítulo apresentamos os tipos básicos introduzidos pela linguagem. Para além disso, o capítulo analisa o impacto da tipificação na linguagem e mostra como o compilador é capaz de inferir os tipos de expressões em vários cenários (dedução esta que contribui para uma redução do código escrito, sem que isso implique uma perda de tipificação dos nossos programas). Finalmente, o capítulo ilustra a conversão explícita de tipos, uma operação que pode ser útil em alguns cenários específicos.
CAPÍTULO 3 – FUNÇÕES À semelhança do que acontece em JavaScript, as funções também desempenham um papel importante em TypeScript. Depois de apresentar a sintaxe geral associada à definição deste tipo de elementos, o capítulo tece várias considerações sobre o uso de parâmetros em funções. Para além disso, existe tempo para analisarmos o uso de funções arrow em TypeScript e para vermos como efetuar o overload de funções nesta linguagem.
CAPÍTULO 4 – INTERFACES As interfaces desempenham um papel importante na linguagem TypeScript. Como veremos neste capítulo, as interfaces permitem-nos definir contratos que podem ser utilizados na validação de código. Assim, o capítulo começa por apresentar as principais caraterísticas e vantagens inerentes ao uso deste tipo de elementos para, em seguida, se centrar na sintaxe usada na sua definição. Neste capítulo analisamos ainda várias questões relacionadas com a compatibilidade entre interfaces e vários tipos de objetos como, por exemplo, objetos literais ou funções.
CAPÍTULO 5 – CLASSES Ao longo deste capítulo apresentamos as principais funcionalidades disponibilizadas pela linguagem no que diz respeito ao uso de classes. Apesar de o JavaScript não suportar a definição de classes semelhante às existentes nas linguagens orientadas a objetos tradicionais, a verdade é que o ECMAScript 2015 acabou por introduzir algumas © FCA – Editora de Informática
XII
TYPESCRIPT – O JAVASCRIPT MODERNO PARA CRIAÇÃO DE APLICAÇÕES
abstrações que simplificam a criação deste tipo de elementos. Como veremos, estas abstrações são reutilizadas e estendidas pelo TypeScript.
CAPÍTULO 6 – OUTROS TIPOS Neste capítulo concentramos a nossa atenção num conjunto de tipos que, apesar de não serem utilizados frequentemente no dia a dia, são importantes na concretização de cenários mais exóticos. Assim, o capítulo começa por apresentar as uniões e interseções para, em seguida, abordar o uso de string literal types, símbolos e iterators, e encerra com a apresentação das principais funcionalidades e caraterísticas associadas ao uso de genéricos.
CAPÍTULO 7 – MÓDULOS O penúltimo capítulo do livro foca-se na criação e uso de módulos. Uma vez mais, estamos perante um conceito que foi introduzido pelo ECMAScript 2015 e que também pode ser utilizado diretamente em TypeScript. Depois de apresentarmos o conceito de módulo e as principais particularidades inerentes à sua sintaxe, terminamos o capítulo com a apresentação de algumas recomendações que serão importantes na definição deste tipo de elementos.
CAPÍTULO 8 – PROJETO: GESTÃO DE CONTACTOS No último capítulo, os vários conceitos e funcionalidades da linguagem TypeScript previamente apresentados são utilizados para criar uma aplicação que efetua a gestão de contactos de um hipotético grupo de alunos de uma escola. Ao longo do capítulo, utilizaremos esta aplicação para mostrar como podemos organizar o código escrito em TypeScript no mundo real e como esta linguagem pode ser também usada para todas as tarefas que, tipicamente, são efetuadas através de JavaScript. No final deste capítulo, e se tivermos feito corretamente o nosso trabalho, não devem restar grandes dúvidas quanto às vantagens inerentes ao uso desta linguagem: para além do aumento da produtividade durante a codificação, é esperada uma redução do número de bugs que venham a ocorrer em runtime.
SUPORTE Este livro foi escrito com base na versão 2.0 da linguagem TypeScript. Se, por acaso, o leitor encontrar informação que lhe pareça incorreta, ou se tiver sugestões em relação ao conteúdo de alguma secção do livro, então não hesite e envie um email com as suas questões para labreu@gmail.com. Eventuais alterações e erratas serão publicadas no site da editora em http://www.fca.pt. © FCA – Editora de Informática
1
INTRODUÇÃO
1
O TypeScript é uma linguagem open source, criada e desenvolvida pela Microsoft, que é compilada (transcompiled) para JavaScript. Apresenta-se como uma solução para alguns dos problemas inerentes aos projetos JavaScript com alguma dimensão. A linguagem surgiu para dar resposta a várias questões detetadas internamente com base no feedback de várias equipas da Microsoft que necessitavam de manter projetos com código escrito em JavaScript. Ao longo deste livro, apresentamos as principais caraterísticas e funcionalidades disponibilizadas por esta nova linguagem e vemos como ela pode ser utilizada para aumentar a robustez e facilitar a manutenção do código JavaScript que escrevemos.
1.1
PORQUÊ UTILIZAR TYPESCRIPT?
Como referimos, o TypeScript pode ser visto como um superset da linguagem JavaScript, em que a tipificação dos dados assume um papel muito importante. Para além da tipificação, a linguagem oferece um conjunto de estruturas específicas que procuram simplificar a concretização de cenários complexos. Algumas destas estruturas (ex.: classes) podem ser convertidas diretamente em JavaScript (tudo depende da versão de JavaScript que geramos). Outras não, são apenas consumidas pelo compilador. As semelhanças entre o JavaScript e o TypeScript contribuem para facilitar a adoção deste último por parte de todos os programadores que já possuem alguma experiência com JavaScript. A possibilidade de especificação de tipos em TypeScript contribui para reduzir o número de bugs no código final e para aumentar a produtividade do programador. Para além dos tipos base introduzidos, a linguagem permite a criação de novos tipos personalizados. A definição do tipo de uma variável possibilita ao compilador de TypeScript verificar se os valores atribuídos a essa variável são, ou não, desse tipo. Quando isso não acontece, o compilador produz automaticamente um erro durante o processo de compilação. Portanto, a tipificação do código JavaScript faz com que eventuais erros passem a ser detetados aquando da compilação e não em runtime, como acontece quando escrevemos código não tipificado em JavaScript. Atualmente, existem vários editores que suportam a escrita de código TypeScript. Para além de disponibilizarem IntelliSense e serem capazes de detetar eventuais erros que possam ocorrer aquando da escrita do código, muitos destes editores permitem a navegação entre os vários tipos que compõem a aplicação atual (o que pode ser útil em © FCA – Editora de Informática
2
TYPESCRIPT – O JAVASCRIPT MODERNO PARA CRIAÇÃO DE APLICAÇÕES
alguns casos, por exemplo, quando necessitamos de investigar o código existente no interior de uma determinada função que está a ser invocada num determinado ponto). Nesta altura, importa ainda salientar que apesar de o TypeScript ser desenvolvido pela Microsoft, estamos perante uma linguagem que pode ser utilizada em vários sistemas operativos (Windows, Linux e OS X). Aquando da escrita deste livro, a versão 2.0 do compilador TypeScript era capaz de efetuar a compilação de código TypeScript em ECMAScript 3, ECMAScript 5 ou ECMAScript 2015 (incialmente designada por ECMAScript 6, esta versão da linguagem é geralmente designada por JavaScript 6). Portanto, a versão do compilador na altura era capaz de produzir código compatível com a versão mais atual da linguagem JavaScript. Nesta fase, pensamos que as várias vantagens apresentadas até ao momento terão sido mais do que suficientes para “convencerem” o leitor acerca das vantagens do uso desta linguagem, pelo que está na altura de avançarmos e de vermos como podemos começar a utilizá-la no dia a dia.
1.2
INSTALAÇÃO
Atualmente, a instalação do TypeScript em Windows pode ser feita de duas formas. O leitor interessado em utilizar o Visual Studio pode efetuar a instalação de um plugin de TypeScript, que pode ser obtido gratuitamente a partir do seu site oficial (em https://www.typescriptlang.org/index.html#download-links). Como se verifica através da Figura 1.1, já existem plugins para vários editores.
FIGURA 1.1 – Plugins para vários editores de TypeScript
© FCA – Editora de Informática
INTRODUÇÃO
3
A instalação do plugin para o Visual Studio resume-se à instalação do componente associado a um dos links apresentados no site da Figura 1.1 e garante a instalação de todos so componentes necessários. Na realidade, o Visual Studio 2015 já suporta o TypeScript, mas a instalação do pacote obtido a partir do link apresentado na Figura 1.1 garante a utilização da versão mais recente da linguagem. Se o leitor optar por não utilizar o Visual Studio, então a forma mais simples de instalar o TypeScript passa pelo uso do gestor de pacotes do Node.js (utilitário npm). Este utilitário é instalado automaticamente aquando da instalação do Node.js, que, por sua vez, pode ser obtido gratuitamente a partir de https://nodejs.org. O Node.js é um ambiente de execução que tem alcançado muita popularidade nos últimos anos e que permite a execução de aplicações escritas em JavaScript. Para além de ser utilizado como ambiente de runtime para várias aplicações Web escritas em JavaScript, possibilita o uso de várias ferramentas e utilitários que simplificam a vida do programador de JavaScript (ex.: Gulp, Grunt, Bower, etc.). A análise deste ambiente de execução está fora do âmbito deste livro, pelo que recomendamos aos eventuais interessados a leitura de Node.js – Construção de Aplicações Web, também publicado pela FCA. Partindo do pressuposto de que já temos o Node.js corretamente instalado e configurado, poderemos instalar o TypeScript através da introdução do comando npm install typescript –g. O excerto seguinte apresenta os resultados obtidos quando executamos este comando numa consola PowerShell num sistema operativo Windows: PS F:\Dropbox\Dropbox\typescript\code> npm install typescript -g C:\Users\luisabreu\AppData\Roaming\npm\tsc -> C:\Users\luisabreu\AppData\Roaming\npm\node_modules\typescript\bin\tsc C:\Users\luisabreu\AppData\Roaming\npm\tsserver -> C:\Users\luisabreu\AppData\Roaming\npm\node_modules\typescript\bin\ts server C:\Users\luisabreu\AppData\Roaming\npm └── typescript@2.0.0
A partir desta altura, temos acesso ao compilador de TypeScript (tsc), que nos permite efetuar a compilação (transpilation) de código TypeScript em código JavaScript. Este utilitário pode ser usado com ou sem parâmetros. Se estivermos interessados em analisar todos os parâmetros que podem ser passados a este utilitário, então deveremos recorrer ao parâmetro help: PS F:\Dropbox\Dropbox\typescript\code> tsc --help Version 1.8.10 Syntax:
tsc [options] [file ...] © FCA – Editora de Informática
4
TYPESCRIPT – O JAVASCRIPT MODERNO PARA CRIAÇÃO DE APLICAÇÕES
Examples: tsc hello.ts tsc --out file.js file.ts tsc @args.txt Options: --allowJs compiled.
Allow javascript files to be
--allowSyntheticDefaultImports Allow default imports from modules with no default export. This does not affect cod e emit, just typechecking. --allowUnreachableCode code.
Do not report errors on unreachable
…
Para além dos parâmetros que podem ser utilizados (e que foram omitidos do excerto anterior por questões de espaço), o texto informativo devolvido pelo utilitário apresenta alguns exemplos simples de compilação, como a compilação de um ficheiro designado por hello.ts que pode ser efetuada através do comando tsc hello.tcs.
1.3
EDITOR VISUAL STUDIO CODE
Depois de instalarmos o TypeScript, é chegada a hora de iniciarmos a escrita de código. Antes disso, temos, contudo, de escolher um editor. Como foi possível verificar através da Figura 1.1, a crescente popularidade do TypeScript contribuiu para que existam plugins para cada vez mais editores. Para além de IntelliSense, existem vários editores que são capazes de compilar automaticamente o código quando este é alterado. Alguns vão um pouco mais longe e permitem até efetuar o debugging de código escrito nesse formato. Apesar de existirem várias boas opções (Visual Studio, Sublime ou WebStorm, por exemplo), neste livro optámos por utilizar o Visual Studio Code (também conhecido por VS Code). Este editor é gratuito e pode ser utilizado em vários sistemas operativos (Windows, Linux e OS X). Para além de fornecer bom suporte ao nível do IntelliSense, este editor permite efetuar o debugging de código TypeScript. A Figura 1.2 ilustra o seu aspeto depois de ter sido utilizado para abrir os conteúdos existentes no interior de uma pasta. Neste caso, estamos a editar a pasta Code (note-se como esta informação é apresentada no painel Explorer – painel aberto na Figura 1.2 e que é controlado pelo primeiro ícone da barra vertical preta apresentada nessa figura).
© FCA – Editora de Informática
3
FUNÇÕES
1
As funções sempre desempenharam um papel importante em JavaScript. Portanto, não será de estranhar que a linguagem TypeScript disponibilize um conjunto de funcionalidades que simplifiquem a sua criação e reduzam o número de erros associados ao seu uso. Ao longo deste capítulo, analisaremos as principais caraterísticas associadas à criação e utilização deste tipo de elementos em TypeScript.
3.1
INTRODUÇÃO
A especificação de tipos suportada pela linguagem TypeScript também se aplica à definição de funções. Para além de podermos definir o tipo de parâmetros e o tipo de retorno de uma função, a linguagem permite a criação de novos tipos baseados na assinatura de uma função (na secção 3.3 analisaremos este tópico detalhadamente). Como seria de esperar, as novas funcionalidades introduzidas pela especificação ECMAScript 2015 também são suportadas pelo TypeScript. Assim, a iniciali-zação de parâmetros com valores predefinidos (secção 3.4.2), a utilização de parâmetros rest (secção 3.4.3) ou as funções arrow (secção 3.5) são funcionalidades totalmente suportadas pela linguagem. Para além disso, são introduzidas outras funcionalidades interessantes, como, por exemplo, a definição de parâmetros opcionais ou a definição de overloads de funções (secção 3.6).
3.2
ESPECIFICAÇÃO DE TIPOS
Como mencionámos, a linguagem permite-nos indicar os tipos de parâmetros e o tipo de retorno de uma função. No seguinte excerto ilustramos esta funcionalidade através da definição de uma função designada por dizOla: function dizOla(nome: string): string{ return `Olá, ${nome}`; }
No excerto anterior criámos uma função nomeada (tipicamente designada na literatura inglesa por named function). Neste caso, a função apresentada espera um valor © FCA – Editora de Informática
38
TYPESCRIPT – O JAVASCRIPT MODERNO PARA CRIAÇÃO DE APLICAÇÕES
do tipo string (parâmetro nome) e devolve um valor também desse tipo. Como é possível verificar, a sintaxe utilizada na sua definição é muito semelhante à apresentada para a declaração de variáveis (ver Capítulo 2): os parâmetros são declarados através da combinação nome:tipo, sendo o tipo de retorno indicado após a lista de parâmetros da função. Se estivermos perante funções que não devolvem valores, então o tipo de retorno a utilizar será o tipo void: function imprimeMsg(msg:string):void{ console.log(…) }
Para além de funções nomeadas, o TypeScript suporta a definição e o uso de funções anónimas. Por exemplo, o seguinte excerto define a função anterior sob a forma de uma função anónima: let imprimeMsg = function (msg:string):void{ console.log(…) }
Na prática, ambos os excertos produzem resultados semelhantes (note-se que isso não seria verdade se imprimeMsg tivesse sido declarada através do termo reservado var). Até agora, todos os exemplos de funções apresentados recorreram à sintaxe tradicional associada à escrita de funções (sintaxe essa que foi herdada do JavaScript e melhorada com o suporte a tipos). O TypeScript também suporta o uso das chamadas funções arrow, que permitem simplificar a escrita de funções anónimas. Esta funcionalidade é igualmente suportada pelo ECMAScript 2015 e será analisada em detalhe na secção 3.5.
3.3
FUNÇÕES COMO TIPOS
Para além de definir os tipos dos parâmetros e os tipos dos valores devolvidos, as funções podem ser usadas para definir novos tipos que, por sua vez, são utilizados para tipificar outras variáveis ou parâmetros. Quando usamos uma função como tipo, estamos apenas a descrever a assinatura de uma função que espera determinados parâmetros e devolve um elemento de um determinado tipo. O seguinte excerto ilustra a sintaxe usada na definição deste género de tipos (cap03/funcoestipo.ts): let impressora : (nome: string) => void; © FCA – Editora de Informática
FUNÇÕES
39
impressora é uma variável que espera uma referência para uma função que recebe uma string através de um parâmetro e que não devolve nenhum valor (uso do tipo void). Apesar de semelhante, importa não confundir a sintaxe usada na definição de tipos deste género com a sintaxe usada na definição de funções arrow, que serão abordadas na secção 3.5. Neste caso, em vez do corpo da função, temos apenas uma referência para o tipo devolvido pela função.
Em seguida, apresentamos código que mostra como podemos inicializar a variável impressora com uma função válida: impressora = function(info:string){ console.log(info); }; impressora("teste");
Neste exemplo, a variável impressora referencia uma função definida à custa de uma função anónima. Esta limita-se apenas a imprimir uma string na consola, que lhe é passada através de um parâmetro. Apesar de apenas apresentarmos as funções arrow na secção 3.5, vamos aproveitar este momento para alterar o exemplo anterior e mostrar como poderíamos inicializar a variável impressora através de uma função deste tipo: impressora = (info:string) => console.log(info);
Como é possível verificar, o recurso a uma função arrow permitiu-nos poupar algum código e, na nossa opinião, não dificulta a leitura do código. Como mencionámos anteriormente, a sintaxe utilizada na escrita destas funções possui algumas semelhanças com a forma como tipificamos uma função. Contudo, neste caso a => é seguida de uma instrução em vez do nome de um tipo. O tipo definido à custa de uma assinatura de uma função é composto por duas partes: a primeira identifica os tipos utilizados nos parâmetros, sendo a segunda responsável por identificar o tipo de retorno dessa função. Como é possível aferir através do exemplo anterior, os nomes dos parâmetros não são utilizados pelo compilador quando este verifica se uma função é, ou não, compatível com um determinado tipo. Na realidade, o compilador limita-se a verificar se os tipos dos parâmetros esperados são, ou não, compatíveis com os utilizados na assinatura. Quando isso acontece (ou seja, quando os tipos dos parâmetros especificados pela função são compatíveis com os especificados pela assinatura), o compilador procede ainda à validação do tipo de retorno da função. No exemplo anterior, o uso do tipo void indica que estamos perante uma função que não devolve nenhum elemento. Na prática, isto significa que qualquer função cujo primeiro parâmetro seja do tipo string, pode ser considerada compatível com o tipo da variável impressora. © FCA – Editora de Informática
40
TYPESCRIPT – O JAVASCRIPT MODERNO PARA CRIAÇÃO DE APLICAÇÕES
Se o tipo de retorno usado na assinatura fosse diferente, então o compilador encarregar-se-ia de verificar se o tipo especificado na função seria, ou não, compatível com o indicado pela assinatura. Analisemos o seguinte exemplo (cap03/funcoestipo.ts): let outra : (nome:string) => string; outra = (info: string): void => console.log(info); //erro outra = (info: string): number => 2; //erro outra = (info: string): string => info;
Neste caso, a variável outra apenas pode referenciar funções que esperam um parâmetro do tipo string e que devolvem uma string. É por isso que as primeiras duas atribuições do exemplo anterior resultam num erro de compilação, uma vez que os tipos de retorno indicados (void e number, respetivamente) não são do tipo indicado pela assinatura da função (neste caso, string).
3.3.1
INFERÊNCIA DE TIPOS
À semelhança do que acontece com variáveis inicializadas com valores de um dos tipos base suportados pela linguagem, existem vários cenários em que o compilador é capaz de inferir os tipos utilizados nas expressões; analisemos os seguintes exemplos (cap03/funcoesinferir.ts): let def1 = (nome: string) => `Olá, {$nome}`; let def2 : (nome:string) => string; def2 = function(info){ return `Olá, ${info}`; }
O tipo da variável def1 não foi explicitado pelo programador. Uma vez que ela foi inicializada aquando da sua declaração, o compilador é capaz de inferir o seu tipo (como seria de esperar, o tipo será o resultante da avaliação dessa expressão de inicialização). Neste caso, a expressão de inicialização utilizada foi uma função arrow, que recebe um parâmetro do tipo string e que devolve também uma string. Note-se ainda como esta função indica apenas o tipo do parâmetro passado ao método, sendo o seu tipo de retorno inferido a partir da expressão que define o seu corpo. Na prática, isto significa que a variável def1 possui o seguinte tipo: (nome:string) => string
Por sua vez, a declaração da variável def2 indica explicitamente o seu tipo (neste caso, estamos a falar de uma função que espera uma string e não devolve nenhum valor). Esta declaração permite-nos omitir os tipos utilizados na função anónima que lhe é atribuída, já que a informação presente na sua declaração é mais do que suficiente para que o compilador efetue essa dedução. © FCA – Editora de Informática
FUNÇÕES
41
Apesar de simples, os exemplos anteriores mostram como a estratégia de inferência (ou dedução) de tipos utilizada pelo compilador permite omitir os tipos em alguns cenários, sem que isso implique uma perda ao nível da tipificação final do programa.
3.4
PARÂMETROS
Como seria de esperar, o TypeScript suporta muitas das novidades introduzidas pelo ECMAScript 2015 relacionadas com o uso de parâmetros. Portanto, funcionalidades como a aplicação de valores predefinidos a parâmetros ou parâmetros do tipo rest são totalmente suportadas pela linguagem. Para além disso, o TypeScript suporta outra funcionalidade não definida na especificação ECMAScript 2015: estamos a falar da definição de parâmetros opcionais. Nas próximas secções analisaremos detalhadamente todas estas funcionalidades e a forma como podem influenciar a assinatura de uma função tipo em TypeScript.
3.4.1
PARÂMETROS OPCIONAIS
Ao contrário do que acontece em JavaScript, os parâmetros são, por predefinição, obrigatórios em TypeScript. Por outras palavras, se uma função define explicitamente dois parâmetros, então, para que a sua invocação seja considerada válida, tem de ser efetuada com dois valores compatíveis com os tipos desses parâmetros. O seguinte exemplo ilustra este ponto (cap03/parametrosopcionais.ts): function obrigatorios(nome:string, idade: number):void{ console.log(`Olá, ${nome}. Tem ${idade} anos.`); } obrigatorios("Rita"); //valido js, erro em TypeScript obrigatorios("Joana", 39);
A função obrigatorios espera dois parâmetros do tipo string e number. Apesar de ser considerada válida em JavaScript, a primeira invocação resulta num erro de compilação em TypeScript. Por outro lado, a segunda invocação não produz nenhum erro. Em abono da verdade, temos de efetuar uma pequena correção à afirmação apresentada no parágrafo anterior. Isto porque a obrigatoriedade de fornecimento de um valor a um parâmetro também pode ser satisfeita através da utilização dos valores undefined e null, conforme ilustrado a sguir: obrigatorios(null, null); obrigatorios(undefined, undefined);
© FCA – Editora de Informática
42
TYPESCRIPT – O JAVASCRIPT MODERNO PARA CRIAÇÃO DE APLICAÇÕES
A validade desta atribuição prende-se com a herança do comportamento existente em JavaScript, em que o valor undefined é sinónimo de uma variável declarada, mas não inicializada e o valor null representa a atribuição de um valor “vazio” (ao leitor interessado em obter mais detalhes sobre este comportamento, recomendamos a leitura do livro JavaScript, também da FCA). Ignorando estes casos particulares, parece-nos óbvio que a primeira invocação só será possível depois de transformar o parâmetro idade num parâmetro opcional. Para isso, temos apenas de adicionar o caráter ? ao seu nome: function obrigatorios(nome:string, idade?: number):void{ console.log(`Olá, ${nome}. Tem ${idade} anos.`); } obrigatorios("Rita"); //ok, idade possui valor undefined
A partir desta altura, o parâmetro idade deixou de ser obrigatório. Assim, se omitirmos o seu valor, passará a possuir o valor undefined no interior da função (este é, aliás, o comportamento predefinido de JavaScript). Uma função pode definir vários parâmetros opcionais, devendo estes serem sempre colocados depois dos parâmetros obrigatórios. Por outras palavras, a seguinte função não é considerada válida em TypeScript: function invalida(nome?: string, idade:number){ } Modo de compilação strict null checking Como mencionámos, a versão 2.0 da linguagem introduz o modo de compilação strict null checking. Este modo introduz várias restrições às atribuições que envolvem os valores null e undefined, permitindo que sejam efetuadas apenas quando os respetivos tipos fazem parte da lista de tipos do parâmetro. Na prática, isto significa que as instruções (apresentada num dos exemplos anteriores) obrigatórios(null, null); obrigatórios(undefined, undefined); produzem erros de compilação. No primeiro caso, a atribuição dos valores null a cada um dos parâmetros só seria permitida se a função tivesse a seguinte assinatura: let assinatura: (nome:string | null, idade?: number | null) => void; Por sua vez, a segunda invocação também produziria erros de compilação. Contudo, neste caso, o problema reside apenas no valor passado ao primeiro parâmetro, já que, por predefinição, o compilador adiciona o tipo undefined à lista de tipos de todos os parâmetros opcionais. Portanto, para que a segunda invocação fosse considerada válida, deveríamos ter o seguinte: let assinatura: (nome:string | undefined, idade?: number)=> void;
© FCA – Editora de Informática
FUNÇÕES
3.4.2
43
PARÂMETROS COM VALORES PREDEFINIDOS
Se quisermos, podemos atribuir um valor predefinido a um parâmetro aquando da sua definição. Nesses casos, esse valor é utilizado no interior da função quando não atribuímos explicitamente um valor ao parâmetro ou quando esse valor é alimentado com o valor undefined. Analisemos o seguinte exemplo (cap03/parametrosvalorespredefinidos.ts): function predefinidos(nome:string = 'Luis', idade = 20){ console.log(`Nome: ${nome}; Idade: ${idade}`); } predefinidos(); //Nome: Luis; Idade: 20 predefinidos("João"); //Nome: João; Idade: 20 predefinidos("João", 30); //Nome: João; Idade: 30 predefinidos(undefined, 30); //Nome: Luis; Idade: 30
Neste caso, estamos perante uma função que declara dois parâmetros, sendo que ambos possuem valores predefinidos. A atribuição de um valor predefinido ao segundo parâmetro mostra que é possível omitir a especificação do seu tipo (uma vez mais, delegamos a dedução do tipo ao compilador). O excerto anterior apresenta ainda alguns exemplos de invocações válidas da função anterior. Nesta altura, importa salientar que a existência de parâmetros com valores predefinidos não invalida a regra apresentada na secção 3.4.1 (sobre o posicionamento de parâmetros opcionais), ou seja, continuam a ter de ser definidos depois dos parâmetros obrigatórios, mesmo quando estes possuem valores predefinidos. Outro aspeto interessante relacionado com o uso de valores predefinidos reside no facto de estes gerarem assinaturas compatíveis com as geradas pelas funções que usam parâmetros opcionais (isto, claro, quando esses parâmetros com valores predefinidos são colocados no final da lista de parâmetros da função). Modo de compilação strict null checking À primeira vista, o leitor poderia ser levado a crer que a última invocação apresentada deveria produzir um erro de compilação quando ativamos o modo de compilação strict null checking: predefinidos(undefined, 30); //Nome: Luis; Idade: 30 Afinal de contas, nenhum dos parâmetros possui o tipo undefined na sua lista de tipos. Apesar de isso ser verdade, importa também reter que, neste caso, a função possui apenas dois parâmetros e que ambos possuem valores predefinidos. Portanto, na prática, o compilador considera a assinatura da função predefinidos como sendo compatível com a seguinte assinatura: (nome?: string, idade?: number) => void;
© FCA – Editora de Informática
44
TYPESCRIPT – O JAVASCRIPT MODERNO PARA CRIAÇÃO DE APLICAÇÕES
A partir desta altura, e se tivermos em atenção o que foi referido na secção 3.4.1, é fácil perceber o porquê de a invocação anterior do método não resultar num erro de compilação.
O seguinte exemplo ilustra este ponto (cap03/parametrosvalorespredefinidos2.ts): 'use strict'; function opcionais(nome: string, idade?:number){} function pred(nome:string,idade = 10){} let demo: (nome:string, idade?:number) => void; demo = opcionais; //OK demo = pred; //OK
Ambas as funções são compatíveis com o tipo associado à variável demo. Apesar de o parâmetro idade da função pred possuir um valor predefinido, no que diz respeito à criação de um tipo a sua colocação no final da lista de parâmetros resulta numa assinatura equivalente à produzida pelo uso de um parâmetro opcional. Uma vez mais, importa salientar que este comportamento apenas ocorre para parâmetros com valores predefinidos colocados depois dos parâmetros obrigatórios. Se os parâmetros com valores predefinidos fossem colocados antes dos parâmetros obrigatórios, então a assinatura da função seria diferente, conforme é possível aferir a partir do seguinte excerto: 'use strict'; function pred3(nome = "Luis", idade: number){} pred3("L", 10); pred3(20); //erro pred3(undefined, 20);
Neste caso, o tipo de pred3 é representado pela seguinte assinatura: (nome:string, idade:number) => void
Ou seja, a atribuição de um valor predefinido a um parâmetro que não ocupa a última posição da lista de parâmetros não o transforma num parâmetro opcional. Na prática, isto significa que a função tem sempre de ser invocada com dois valores compatíveis com os tipos especificados pelos parâmetros nome e idade. Se não quisermos passar um valor ao parâmetro nome, então seremos obrigados a passar o valor undefined. Nesta altura, importa notar que o valor undefined passado ao primeiro parâmetro é considerado válido mesmo quando compilamos o código em modo strict null checking.
© FCA – Editora de Informática
8
PROJETO: GESTÃO DE CONTACTOS 1
2
Agora que analisámos as principais funcionalidades introduzidas pela linguagem TypeScript, estamos já em condições de reutilizar os conhecimentos obtidos na construção de uma aplicação que seja responsável por efetuar a gestão hipotética dos contactos dos alunos de uma escola. Apesar de neste capítulo nos termos centrado apenas no lado cliente, a verdade é que os passos apresentados podem ser facilmente adaptados por quem, por exemplo, pretende escrever código TypeScript para ser consumido a partir do ambiente de runtime Node.js.
8.1
INTRODUÇÃO
Antes de analisar o código que é utilizado neste projeto, vamos efetuar uma breve apresentação da aplicação final e das suas principais funcionalidades. A Figura 8.1 apresenta o aspeto final do projeto.
FIGURA 8.1 – Aspeto da aplicação de gestão de contactos de alunos
© FCA – Editora de Informática
128
TYPESCRIPT – O JAVASCRIPT MODERNO PARA CRIAÇÃO DE APLICAÇÕES
Cada aluno é caraterizado por um nome e uma lista de contactos (neste caso, limitados a telefones e emails). A página apresentada na Figura 8.1 encontra-se dividida em duas secções. No lado esquerdo encontramos uma lista que, por predefinição, carrega todos os alunos existentes. Esta lista pode ser filtrada através do uso da caixa de texto situada no topo dessa secção. No lado direito temos acesso a uma ficha de contactos, que pode ser utilizada para introduzir um novo aluno ou editar os dados de um aluno que foi registado previamente. Do ponto de vista prático, todo o código cliente utilizado pela página será escrito em TypeScript e reutilizará algumas das funcionalidades disponibilizadas pelas livrarias jQuery, knockout e SystemJS. A livraria jQuery é utilizada para simplificar a interação com os elementos mantidos na página (ex.: colocar o foco num controlo). Por sua vez, knockout é uma livraria que facilita a implementação do padrão MVVM (Model-View-ViewModel) e que será utilizada para permitir o estabelecimento de relacionamentos de binding entre os controlos e um modelo. Finalmente, uma vez que utilizámos módulos para encapsular os vários componentes utilizados pela aplicação, vamos recorrer à livraria SystemJS para efetuar o carregamento dinâmico dos módulos que compõem a nossa aplicação. No que diz respeito à definição do layout da página (apresentado na Figura 8.1), optámos por recorrer à livraria bootstrap, dado que é considerada a referência para este tipo de tarefas. Toda a gestão das dependências utilizadas ficará a cargo do gestor de pacotes do Node.js (npm). Por outras palavras, a obtenção das livrarias clientes utilizadas será feita através do utilitário npm. Este passa a estar disponível depois de instalarmos o Node.js, que pode ser obtido gratuitamente a partir de https://nodejs.org/.
8.2
PREPARAÇÃO
Depois de instalar o ambiente de runtime do Node.js, estaremos em condições de iniciar o processo de criação da nossa aplicação. Uma vez que optámos por instalar todas as nossas dependências a partir do gestor de pacotes do Node.js, o primeiro passo é adicionar um ficheiro de configuração, designado por package.json, que nos permite listar todas as dependências necessárias à criação da nossa aplicação. A forma mais fácil de criar este ficheiro passa pela execução do comando npm init, conforme ilustrado pelo seguinte excerto: PS C:\code\angular2\democontactos> npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. © FCA – Editora de Informática