Unions
1) Usamos o conceito de união para definir que o parâmetro poderá ser do
tipo string ou number.
2) Usamos a função com os
dois tipos de parâmetros.
3) O tipo “any” poderia ter sido usado, contudo, o usuário tem a possibilidade
de usar um tipo boolean, o que não é o desejado.
interface Bird { raca: string; fly(); layEggs(); } interface Fish { raca: string; swim(); layEggs(); }
function getSmallPet(): Fish | Bird {
let f: Fish;
return f; }
let pet = getSmallPet();
pet.layEggs(); // Correto
pet.swim(); // Erro
class Cliente {
endereco: string;
constructor(public nome: string){} }
class Fornecedor {
telefone: string;
constructor(public nome: string){} }
let x1: Cliente|Fornecedor;
x1 = new Cliente('Jorge');
x1.endereco = 'Rua do centro';
console.log(x1.endereco);
x1 = new Fornecedor('Farmácia');
x1.telefone = '2222-2222';
console.log(x1.telefone);
Outros tipo
Unions
1) Quando recorremos a uma união para definir o tipo de um objeto, o compilador só permite o uso de membros comum
ao tipo instanciado.
2) Quando recorremos a valores que tem o tipo união, somente os
membros em comum dos tipos podem ser
let aa: number = 9;
console.log(typeof aa); // "mumber"
console.log(typeof {}); // "object"
console.log(typeof []); // "object"
console.log(typeof new Date()); // "object"
console.log(typeof new RegExp('')); // "object"
console.log(typeof null); // "object"
Outros tipo
Guardas
1) Não descobrimos qual a classe do objeto, somos informados apenas que é
um objeto.
• Em Javascript é normal verificar se a variável é de um determinado tipo. Isso é necessário, pois as variáveis podem ser uniões de tipo. Para isso temos as palavras reservadas typeof, instanceof e in.
• O operador typeof trabalha em um variável e retorna uma string, indicando o tipo do valor.
• O operador typeof é excelente para reconhecimento de valores primitivos, como os acima citados. Para detecção de valores de referência, entretanto, eles não possuem serventia:
•Para strings, typeof retorna“string.” •Para numbers, typeof retorna“number.” •Para booleans, typeof retorna“boolean.” •Para undefined, typeof retorna“undefined.”
Outros tipo
Guardas
• O operador instanceof distingue instâncias de classes diferentes.
• Ainda temos o operador in para verificar se existe algum determinado membro em um objeto:
console.log({} instanceof Object); // "object"
console.log([] instanceof Array); // "object"
console.log( new Date() instanceof Date); // "object"
console.log(new RegExp('') instanceof RegExp); // "object"
interface Point {
x: number;
y: number;}
interface Point3d extends Point {
z: number;}
function plot(point: Point) {
if ('z' in point) { // point é um `Point3D` } else { // point é um `Point` } }
interface Square { kind: "square"; size: number; } interface Rectangle { kind: "rectangle"; width: number; height: number; } interface Circle { kind: "circle"; radius: number; }
function area(s: Square | Rectangle | Circle) {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.height * s.width;
case "circle": return Math.PI * s.radius ** 2; }
}
let forma: Circle = {kind: "circle", radius: 3.15};
console.log( area(forma) );
Outros tipo
Discriminated unions ou tagged Unions
• Existe um outro recurso de guarda, o qual consiste em criar um campo em comum entre os tipos, assim o compilador é capaz de inferir o tipo de acordo com esse campo em uma condição if ou switch.
interface IPessoa{ nome: string; } interface IAluno{ escola: string; }
function imprimeInfo( estudante : IPessoa & IAluno){
console.log( `${estudante.nome} matriculado na escola
${estudante.escola}` ); }
let jorge = {nome: 'Jorge Silva', escola: 'Militar'};
imprimeInfo(jorge);
Outros tipo
Interseções de tipos
• As interseções (&) são tipos personalizados criados a partir da combinação de vários tipos. Assim, o objeto assume simultaneamente a identidade de todos os tipos e será
Outros tipo
Aliases
• Aliases permite criar nomes alternativos para tipos existentes. Não se trata de um novo tipo, mas são uteis para a redução de código e para melhorar a abstração dos problemas. Pode-se renomear tipos primitivos, uniões, tuplas, entre outros tipos criados pelo usuário.
type Nome = string;
type NomeCorreto = () => string;
type NomeOrNomeCorreto = Nome | NomeCorreto;
function getName(n: NomeOrNomeCorreto): Nome {
if (typeof n === "string") { return n; } else return n(); } type Tree = { value: number; left: Tree; right: Tree; }
type Matriculado = IPessoa & IAluno;
function imprimeNovo( estudante : Matriculado){
console.log( `${estudante.nome} matriculado na escola ${estudante.escola}` ); }
1) Usamos a palavra reservada typepara criar os apelidos. Podemos criar
apelidos utilizando outros apelidos e dar apelidos
Outros tipo
String literal Types
• String literal types permitem criar um novo tipo de dados em que o tipo string recebe exatamente os valores que foram especificados na definição.
let resposta: "Sim" | "Não";
resposta = "Sim";
//resposta = "talvez"; <===Erro
type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
animate(dx: number, dy: number, easing: Easing) {
if (easing === "ease-in") {
// ...
}
else if (easing === "ease-out") { }
else if (easing === "ease-in-out") { }
else {
// error! should not pass null or undefined.
} } }
let button = new UIElement();
button.animate(0, 0, "ease-in");
Outros tipo
Iteradores
• Typescript implementou os conceitos de iterable e iterator. Se o objeto devolver um iterator a partir de uma propriedade que referencia uma função acessada pelo símbolo Symbol.iterator, ele passa a ser um iterable.
• Vários dos tipos introduzidos pela linguagem (ex.: Array, Map, Set, String) são iterables. • A forma mais fácil de iterar sobre essas coleções é através das instruções for..of e for..in
let list = [4, 5, 6];
for (let i in list) {
console.log(i); // "0", "1", "2",
}
for (let i of list) {
console.log(i); // "4", "5", "6"
}
let iterar = {nome: 'Bruno’,
sobrenome: 'Ferreira'};
for (let i in iterar) {
console.log(i); }
for (let i of iterar) {
console.log(i); }
for..of for..in
Acessa o valor Acessa a chave que retorna o valor Aplicado somente a objetos iterables Pode ser aplicado a qualquer objeto Percorre o objeto sem conhecer a forma Percorre o objeto sem conhecer a forma
1) O primeiro laço retorna os índices, o
segundo os valores desses índices.
2) O primeiro laço retorna o nome das
propriedades, o segundo mostra um erro de compilação, pois o objeto não é
class Flies {
fly() {
console.log('voando...'); }
}
class Climbs {
climb() {
console.log(‘subindo na parede'); }
}
Outros tipo
Mixim ou herança múltiplas
• Typescript permite a criação de classes a partir da combinação de outras. Fornecendo assim herança múltipla. Tem-se que seguir três passos para obter o resultado esperado:
1. Utilizar a palavra implements ao invés de extends durante a composição da nova classe;
2. Redeclarar as propriedades e os métodos como propriedades na nova classe.
3. Criar um método chamada applyMixins com os argumentos corretos.
1) Começamos com duas classes simples que representam superpoderes de
class BeetleGuy implements Climbs, Flies { climb: () => void; fly: () => void; sting(){ this.fly(); this.climb();
console.log('ferroa!!'); }
}
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
if (name !== 'constructor') {
derivedCtor.prototype[name] = baseCtor.prototype[name]; }
}); });
}
applyMixins (BeetleGuy, [Climbs, Flies]);
let superBesouro = new BeetleGuy();
superBesouro.sting();
Outros tipo
Mixim ou herança múltiplas
2) Criamos uma terceira classe que herda os poderes das duas primeiras classes (herança múltipla), para qual usamos a
palavra chave implements.
3) Redeclaramos todos os métodos como propriedades,
nesse caso, não existiam
propriedades a serem herdadas. 4) Criamos o seguinte método
para que o compilador consiga fazer a transcrição da herança múltipla para o Javascript. Esse método pode ser criado e inserido
em um módulo para ser aplicado quando necessário.
5) Registramos a classe com herança múltipla utilizando o
método criado e, por fim, utilizamos a nova classe instanciando um objeto.
function compara<T>(parametro1: T, parametro2: T): boolean{
return parametro1 == parametro2; }
console.log(compara<number>(10,20));
console.log(compara<string>('bh','sp’));
console.log(compara(true,true));
Outros tipo
Generics
• Typescript, como outras linguagens oferece o recurso de generics, o qual visa aumentar a
reusabilidade de código tornando os tipos de dados genéricos, isso sem perder a verificação de tipos.
• Um exemplo clássico é o uso de coleções que são capazes de se adaptarem a diferentes tipos de objetos ou tipos primitivos.
• Veja um exemplo abaixo, aonde não queremos usar o tipo any, para dar mais segurança referente os tipos de dados a uma função que compara se os valores são iguais:
1) Introduzimos um ou mais parâmetro de tipos, de qualquer nome (padrão T) que serve
como um placeholder para tipos concretos.
2) Definimos o tipo da função quando ela é invocada. 3) Se omitirmos o placeholder, o compilador tentará inferir o tipo.
interface Molde { <T>(arg: T): T; }
function identidade<T>(arg: T): T {
return arg; }
let myIdentity: Molde = identidade;
console.log(myIdentity<number>(1000));
function identity<T>(arg: T): T {
return arg; }
let myIdentity: <U>(arg: U) => U = identity;
console.log(myIdentity(3000));
Outros tipo
Generics
• Pode-se usar diferentes nomes para os parâmetros de tipos genéricos:
• Pode-se utilizar generics em funções de uma interface, isso pode ser feito de duas formas: 1) Usamos o nome “T” para a definição da função e o nome “Q” para a definição de um tipo que irá receber a função, o qual mantém
a mesma assinatura da função identity.
2) Declaramos o tipo genérico na função da interface. 3) Se omitirmos o placeholder, o compilador tentará inferir o tipo. interface Molde<T> { (arg: T): T; }
function identidade<T>(arg: T): T {
return arg; }
let myIdentity: Molde<number> = identidade;
console.log(myIdentity(1000));
2) Declaramos o tipo genérico na definição da
class Generica<U>{
constructor(public t: U){} }
let g = new Generica<boolean>(true);
console.log(g.t);
class AdicionaGenerico<T> {
valorInicial: T;
add: (x: T, y: T) => T; }
let myGenericNumber = new AdicionaGenerico<number>();
myGenericNumber.valorInicial = 0;
myGenericNumber.add = function(x, y) { return x + y; };
console.log(myGenericNumber.add(100,400));
let stringNumeric = new AdicionaGenerico<string>();
stringNumeric.valorInicial = " ";
stringNumeric.add = function(x, y) { return x + y; };
console.log(stringNumeric.add(stringNumeric.valorInicial, "test"));
• Pode-se utilizar generics em classes, isso pode ser feito da seguinte forma:
• Outro exemplo:
Outros tipo
Generics 1) Classe com o tipo genérico definido na declaração da classe. 2) Instanciamos dois objetos comtipos diferentes.
3) Propriedade definida com
generics no construtor
• O compilador Typescript, em muitas das vezes, não consegue prever se um parâmetro do tipo genérico tem uma determinada propriedade ou método. Veja o exemplo abaixo:
• Contudo, temos que garantir que o tipo passada terá esse método ou propriedade e isso é feito criando uma interface (contrato) em conjunto com a palavra reservada extends durante a
declaração do tipo genérico:
function registrar<T>(arg: T): T {
console.log(arg.length);
return arg; }
Outros tipo
Generics Constraints
1) Essa linha apresenta um erro, o compilador não reconhece o método lenght
interface Comprimento {
length: number; }
function registrar<T extends Comprimento>(arg: T): T {
console.log(arg.length);
return arg; }
• Outro exemplo:
• Podemos aprofundar nos exemplos criando restrições entre os parâmetros de tipo genéricos:
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key]; }
let xs = { a: 1, b: 2, c: 3, d: 4 };
let retorno = getProperty(xs, 'a'); // okay
console.log(retorno);
//retorno = getProperty(xs, 'm'); //Erro: não existe a chave 'm' //console.log(retorno);
Outros tipo
Generics Constraints
2) O tipo k têm os mesmos membros que o elemento T.
interface PropriedadeNome{
nome:string; }
class PessoaFisica{
constructor(public nome: string){} }
class PessoaJuridica{
constructor(public nome: string){} }
function envia<Y extends PropriedadeNome>(obj:Y){
console.log('get:http:\\\\localhost\\8080\\'+obj.nome); }
envia({nome: 'matriz'});
1) O tipo Y tem a propriedade nome.
• Promise é um tipo de dado que tenta solucionar problemas relacionados com tarefas assíncronas em cadeia.
• Esse tipo de dado é responsável por notificar acerca da sua conclusão. Sua especificação é direta
• Promises aceitam uma função por parâmetro. Esta função será executada recebendo duas novas funções em seus argumentos – uma para resolver a promise, outra para rejeitá-la. Caso um erro aconteça dentro de uma promise, ela também será rejeitada.
• Uma promise possui dois métodos principais:
then: Executado quando a promise é resolvida; catch: Executado em caso de rejeição (ou throw).
Outros tipo
Tipo Promises
let pr = new Promise ( (resolve, reject) => {
// resolva ou rejeite
• Exemplo 1:
• Tanto a função resolve quanto a reject podem ser executadas apenas uma vez dentro de uma promise.
• Já o método then pode ser usado várias vezes para a mesma promise.
Outros tipo
Tipo Promises
let pr = new Promise ( (resolve, reject) => {
resolve(10); });
pr.then(valor => console.log(value));
1) Nome padrão das funções para aceitar e
rejeitar a promessa.
2) Quando executarmos a promessa, ela dirá que o processamento foi resolvido e
o valor final é 10.
2) O método then é acionado com a promessa resolvida. A
variável “valor” recupera o retorno da promessa.
let pr2 = new Promise ( function (resolve, reject) {
resolve(100); });
pr2.then( (valor) => {console.log(valor); return 200;}) .then( (v) => console.log(v))
.catch( (err) => console.log('erro!'));
4) Caso o método reject tivesse sido acionado dentro da
promise, o catch seria executado.
function abrirArquivo() : Promise<String>{
return new Promise( (resolve,reject) => {
//abrindo o arquivo...
let correto = true;
if (correto)
resolve("Aberto..")
reject("erro ao abrir o arquivo.") });
}
function lerArquivo() : Promise<String>{
return new Promise( (resolve,reject) => {
//abrindo o arquivo...
let correto = false;
if (correto)
resolve("Lendo..")
reject("erro ao ler o arquivo.") });
}
function fecharArquivo() : Promise<String>{
return new Promise( (resolve,reject) => {
//abrindo o arquivo...
let correto = true;
if (correto)
resolve("Fechando..")
reject("erro ao fechar o arquivo.") });
}
abrirArquivo()
.then( () => lerArquivo()
.then( () => fecharArquivo()
.then( () => console.log('tudo certo!'))
.catch( (errFechar) => console.log(errFechar)) .catch( (errLer) => console.log(errLer) ) ) )
.catch( (errAbrrir) => console.log(errAbrrir));
• Exemplo 2:
Outros tipo
Tipo Promises 1) Geralmente usamos
Promise como retorno de funções.
2) Uma das grandes vantagens de Promise é que podemos encadear ações em ambientes
assíncronos. Contudo, simulamos uma ação de três passos que é o de abrir, ler e
• all: retorna uma promise pendente que será resolvida quando todas as promises passadas por parâmetro (array) forem resolvidas. Ela será rejeitada se qualquer uma das promises rejeitar. • race: retorna uma promise pendente, mas ela será resolvida assim que qualquer uma das
promises enviadas seja resolvida, se uma delas falhar antes de qualquer uma ser resolvida, então essa promise será rejeitada.
Outros tipo
• A modularização é um importante conceito para a organização do código fonte, melhorando a legibilidade e consequentemente a manutenção dos sistemas, a partir da ECMAScript 2015 o Typescript passou a dar suporte a esse conceito;
• O problema é que o conceito foi introduzido recentemente então a comunidade Javascript resolvia essa ausência propondo soluções diferentes, o que gerou diversos padrões (sintaxes) de criação de módulos, ex.:
• CommonsJS – implementação de módulos do NodeJS;
• AMD (Assynchronous Module Definition) – formato popular proposto por alguns fabricantes de browsers;
• UMD (Universal Module Definition) – formato que combina os dois primeiros e podem rodar no NodeJS e no
browser;
• System – formato que suporta vários formatos;
• ES2015 – formato propostos pelo ECMAScript 2015 e implementado pelo TypeScript.
Módulos
• Um módulo pode ser considerado como um arquivo que importa ou exporta código fonte; • Cada módulo tem seu próprio contexto, ou seja, seus elementos são visíveis somente dentro
dele, a menos que os exportemos, assim outros módulos podem acessar esses elementos;
• Um module loader é o responsável por carregar os módulos que um importador está
requisitando. O NodeJS (servidor) possui esse loader, mas os browseres (cliente) não, assim temos que usar bibliotecas externar para resolver esse problema. Exemplos:
Módulos
Introdução
Bibliotecas Formatos suportados
RequireJS AMD
• A exportação de um elemento por um módulo é feita através da palavra reservada export. • A importação de um elemento por um módulo é feita através da palavra reservada import.
Módulos
Sintaxe
import {Carro} from './modelo1';
let uno = new Carro('Uno Hatch', 25000.00);
console.log(`Carro: ${uno.tipo} custa: ${uno.valor}`);
export class Carro{
constructor (public tipo: string, public valor: number){} }
module1.ts
app.ts A importação é feita Informando
entre chaves o que está sendo importado e depois dizemos ao interpretador aonde se encontra o
arquivo do módulo.
O caminho dos módulos podem ser relativos ou absolutos. O primeiro tipo usa três formas: O símbolo “./” identifica um módulo existente na mesma pasta que o arquivo que fez a importação. O símbolo “../” indica que o módulo está
em uma pasta anterior a do arquivo que importou o módulo. O uso do “/” indica que é a pasta raiz da aplicação.
O uso de caminho absoluto são feitos através da propriedade baseUrl no arquivo tsconfig.json.
import {Carro, printer} from './modelo1';
let uno = new Carro('Uno Hatch', 25000.00);
printer(uno);
Módulos
Exportando vários elementos
module1.ts
app.ts 1) O módulo expôs
dois elementos.
2) O arquivo principal está exportando dois elementos do módulo 1.
export class Carro{
constructor (public tipo: string, public valor: number){} }
export function printer(c: Carro){
console.log(`Carro: ${c.tipo} custa: ${c.valor}`) }
class Carro{
constructor (public tipo: string, public valor: number){} }
function printer(c: Carro){
console.log(`Carro: ${c.tipo} custa: ${c.valor}`) }
export {Carro, printer}