DEV Community

Cover image for Typescript - Alguns conceitos básicos
Marlon Frade
Marlon Frade

Posted on

Typescript - Alguns conceitos básicos

👋 Fala Dev,

A seguir teremos anotações sobre typescript, o curso irá abordar os principais conceitos e projetos na prática.

Table of Contents

O que é o Typescript?

  • Typescript é um superset para a linguagem JavaScript
  • Ou seja, adiciona funções ao JavaScript, como a declaração de tipos de variável.
  • Pode ser utilizado no desenvolvimento de projetos com NodeJS e express para criação de APIS, e bibliotecas como React.
  • Precisa ser compilado pra JavaScript, pois não executamos TS;
  • Desenvolvido e mantido pela Microsoft

Exemplo

let x: number = 10;
console.log("antes", x);

x = 16;
console.log("depois", x);
Enter fullscreen mode Exit fullscreen mode

Inferencia X Annotation

Ao trabalhar com projetos em typescript, será necessário estar familiarizado com qual caminho de tipagem seu código irá seguir.

//Inferencia X Annotation

let z = 16; 
// z="teste" -- erro

let y: number = 16;
Enter fullscreen mode Exit fullscreen mode

Ou seja, podemos perceber que na inferência o Typescript entende a qual tipo a variável está sendo atribuída.

Porém cabe ressaltar que nem sempre a inferência vai funcionar, dado os contextos de cada situação, sendo necessário a recorrência da tipagem conforme segundo exemplo.

Analisando Tipagem Básica

let firstName: string = "Marlon";

let age: number = 25;

const isAdmin: boolean = true;

console.log(typeof firstName);

age = 26;

console.log(age);
Enter fullscreen mode Exit fullscreen mode

String !== string - Em javascript nós temos os objetos que são em letra maiúscula, e as variáveis em letras minúsculas, porém em Typescript sempre iremos trabalhar com as letras minúsculas

Lembrando que o console do teu tipo deve ser o tipo que será utilizado para declarar o tipo da variável.

Analisando a Tipagem de Objetos

Array

Para trabalhar com listas e arrays, devemos utilizar a seguinte sintaxe:

const myNumbers: number[] = [1, 2, 3];

//declaramos o array como sendo um array de numbers
//sendo assim já podemos utilizar as propriedades do array como:

console.log(myNumbers);

console.log(typeof myNumbers);

console.log(myNumbers.length);

myNumbers.push(4);

console.log(myNumbers);
Enter fullscreen mode Exit fullscreen mode

Tuple

No python, as tuplas são arrays cujos valores inseridos não podem ser modficados, no typescript, as tuplas terão suas tipagens declaradas dentro do array, como no exemplo abaixo:

let myTuple: [number, string, string[]];

myTuple = [5, "teste", ["a", "b"]];
// myTuple = [true, false, true] -- erro
Enter fullscreen mode Exit fullscreen mode

Object Literals

Ao declarar objetos, uma chaves irá receber a tipagem dos dados, enquanto que a segunda chaves irá receber os dados de informação, por exemplo:

//object literals -> {prop: value}

const user: {
    name: string;
    age: number;
} = {
    name: "Marlon",
    age: 25,
};

console.log(user);

console.log(user.name);

// user.job = "programador" -- erro
Enter fullscreen mode Exit fullscreen mode

Analisando Tipagem Múltipla

Any

O any foi feito pra aceitar qualquer tipo, indo contra a "filosofia" do typescript, mas também para definirmos uma variável que pode receber qualquer tipo de valor.

let a: any = 0;

a = "teste";
a = true;
a = [];
Enter fullscreen mode Exit fullscreen mode

Lembrando que o any não deve ser utilizado em qualquer circunstância, mas sim dentro de um contexto específico onde não se pode prever o tipo do valor retornado ou declarado. Utilizá-lo fora desse contexto pode ser considerado uma má prática.

Union Type

Uma das formas de se unir tipos para criar um outro tipo mais complexo, pode ser chamado de union type. Essa seria uma maneira mais ==typescript== de se resolver os problemas de utilização de any fora de contexto, ou seja, não sabemos se os dados serão retornados em string ou number, aí ao invés de se utilizar o any podemos aderir ao union type, como no caso do id que dependendo do formato do projeto pode ser retornado como string
ou number.

let id: string | number = "1";

id = 200;
// id = true -- erro
Enter fullscreen mode Exit fullscreen mode

Type Alias

Uma maneira eficiente de se criar código limpo e reutilizável é usando o type alias, como uma solução para o caso de decidir adicionar mais um tipo a variável ID do exemplo anterior, não sendo necessário deixar o código extenso, podemos adicionar um alias e reduzir a tipagem:

type myIdType = number | string;

const userId: myIdType = "1";
const productId: myIdType = 200;
const shirId: myIdType = 123;
// id = true -- erro
Enter fullscreen mode Exit fullscreen mode

Enum

Um tipo de dado que enumera uma coleção para trabalharmos dados mais complexos de uma maneira mais simples, por exemplo:

// exemplo: tamanho de roupas (size: Médio, size: Pequeno)

enum Size {
    P = "pequeno",
    M = "Médio",
    G = "Grande",
}

const camisa = {
    name: "Camisa Gola V",
    size: Size.P,
};

console.log(camisa);
Enter fullscreen mode Exit fullscreen mode

Analisando Tipagem Literal

Podemos aplicar valores literais para um tipo, sendo assim impossível que esse valor seja alterado posteriormente.

let teste: "algumvalor";
// teste = "outrovalor" -- erro

teste = "algumvalor";
Enter fullscreen mode Exit fullscreen mode

Geralmente para poder resetar o valor aplicado a sua variável devemos usar o union type para declarar que a variável pode ser null, por exemplo

let teste2: "algumvalor" | null;

teste2 = "algumvalor";
teste2 = null;
Enter fullscreen mode Exit fullscreen mode

Analisando Funções

Função Básica

Para se criar uma função no typescript, devemos usar a mesma semântica do javascript, porém todos os argumentos passados para a função devem ser tipados:

function sum(a: number, b: number) {
    return a + b;
}

console.log(sum(12, 13));
// console.log(sum("12", true)); -- erro
Enter fullscreen mode Exit fullscreen mode

Tipagem do Retorno

Além de informar o tipo do parâmetro que estamos declarando, caso uma função tenha retorno, podemos declarar como no primeiro exemplo, e quando a função não possui retorno utilizamos a declaração do void para fazer a tipagem do retorno da função.

// Estamos informando que o retorno da função 
// deve ser uma string
function sayHelloTo(name: string): string {
    return `Hello ${name}`;
}

console.log(sayHelloTo("Marlon"));

// Quando a função não retornar nada como no 
// exemplo abaixo podemos usar o void
function logger(msg: string): void {
    console.log(msg);
}

logger("Hello World");
Enter fullscreen mode Exit fullscreen mode

Parâmetros opcionais

Caso algum dos parâmetros da função sejam opcionais como no exemplo abaixo, podemos utilizar o ==?== e fazer as devidas tratativas de condicionais.

function greeting(name: string, greet?: string): void {
    console.log(`Olá, ${greet} ${name}`);
    if (greet) {
        console.log(`Olá, ${greet} ${name}`);
    } else {
        console.log(`Olá, ${name}`);
    }
}

greeting("Marlon");
greeting("Marlon", "Sir");
Enter fullscreen mode Exit fullscreen mode

Ao usar o "?", colocamos o greet como opcional, não sendo obrigatório o parâmetro. Porém perceba que há um erro de lógica acima, onde o log do greet virá como undefined, o typescript não aponta esse erro pois colocamos o parâmetro como opcional, sendo responsabilidade do desenvolvedor tratar essa obrigatoriedade, como no exemplo acima.

Interfaces

As interfaces são um recurso do typescript que veio para o javascript, que padronizam algo para que possamos reutilizar como um tipo.

interface MathFunctionsParams {
    n1: number;
    n2: number;
}

// perceba que são dois parâmetros para uma função matemática, porém usando interface(objeto) e enum

function sumNumbers(nums: MathFunctionsParams) {
    return nums.n1 + nums.n2;
}

const result: number = sumNumbers({ n1: 1, n2: 2 });
console.log(result);

// utilizando de outras formas

const someNumbers: MathFunctionsParams = {
    n1: 5,
    n2: 10,
};

function multiplyNumbers(nums: MathFunctionsParams) {
    return nums.n1 * nums.n2;
}

const result2: number = multiplyNumbers(someNumbers);
console.log(result2);
Enter fullscreen mode Exit fullscreen mode

Narrowing

Quando não sabemos qual tipo exato será o parâmetro de uma função, mas precisamos fazer uma tratativa com esses resultados, podemos usar o narrowing do typeof para tratar as condicionais:

function doSomething(info: number | boolean) {
    if (typeof info === "number") {
        console.log(`O número é ${info}`);
        return;
    }
    console.log(`Não foi passado nenhum número`);
}

doSomething(5)
doSomething(true)
Enter fullscreen mode Exit fullscreen mode

Ao verificar o typeof de um parâmetro, estamos usando um narrowing de typeof para garantir que o retorno será de acordo com o parâmetro passado.

Generics

Um outro recurso do typescript é o generics, que para se evitar um any, pode ser aplicado em diferentes situações que possuem a necessidade de se declarar um tipo genérico, que pode receber mais de um tipo de valor não esperado, como uma função que mostra os itens de um array, porém não sabemos se os itens virão como number, string, ao invés do any podemos utilizar o generics para declarar um tipo genérico seguindo a seguinte linha de desenvolvimento

function showArrayItems<T>(arr: T[]) {
    arr.forEach((item) => {
        console.log(item);
    });
}

const a1 = [1, 2, 3];
const a2 = ["1", "2", "3"];

showArrayItems(a1);
showArrayItems(a2);
Enter fullscreen mode Exit fullscreen mode

Usamos a tag <> após o nome da função para remeter a algo genérico, como por exemplo T e U (que são mais utilizados no typescript). No caso acima utilizamos a tag T referenciando dentro do parâmetro para exemplificar que esperamos receber qualquer tipo de dado nesse array, porém sendo mais especificado que um simples any.

Classes

Class é um método de orientação a objeto utilizado para definir novas classes como no exemplo abaixo:

class User {
    name;
    role;
    isApproved;
    constructor(name: string, role: string, isApproved: boolean) {
        this.name = name;
        this.role = role;
        this.isApproved = isApproved;
    }
}

const marlon = new User("Marlon", "admin", true);
console.log(marlon);
Enter fullscreen mode Exit fullscreen mode

Na utilização de classes, precisamos usar um constructor pois o typescript solicita numa class a tipagem dos dados e um inicializador, portanto sendo mais sucinto declarar os tipos num constructor e utilizar a tipagem por inferência, ou seja, declarar no constructor os tipos e atribuir aos valores do objeto.

Podemos também utilizar os constructor para criar métodos, como por exemplo:

class User {
    name;
    role;
    isApproved;
    constructor(name: string, role: string, isApproved: boolean) {
        this.name = name;
        this.role = role;
        this.isApproved = isApproved;
    }
    showUserName() {
        console.log(`O nome do usuário é ${this.name}`);
    }
}

const marlon = new User("Marlon", "admin", true);
marlon.showUserName();
Enter fullscreen mode Exit fullscreen mode

Um outro exemplo utilizando parâmetros:

class User {
    name;
    role;
    isApproved;
    constructor(name: string, role: string, isApproved: boolean) {
        this.name = name;
        this.role = role;
        this.isApproved = isApproved;
    }
    showUserRole(canShow: boolean) {
        if (canShow) {
            console.log(`O role do usuário é ${this.role}`);
            return;
        }
        console.log(`informação restrita!`);
    }
}

const marlon = new User("Marlon", "admin", true);
console.log(marlon);

marlon.showUserRole(true);
marlon.showUserRole(false);
Enter fullscreen mode Exit fullscreen mode

Interfaces em classes

Interfaces é o core da orientação a objetos, geralmente sendo utilizada para ditar como uma classe irá se comportar. Sendo muito útil em projetos onde há classes que são muito parecidas, ou seja, tendo um padrão entre elas. Um exemplo de uma interface de veículos:

interface IVehicle {
    brand: string;
    showBrand(): void;
}

class Car implements IVehicle {
    brand;
    wheels;
    constructor(brand: string, wheels: number) {
        this.brand = brand;
        this.wheels = wheels;
    }
    showBrand(): void {
        console.log(`A marca do carro é ${this.brand}`);
    }
}

const mustang = new Car("Ford", 4);
mustang.showBrand();
Enter fullscreen mode Exit fullscreen mode

Para criar interfaces, utiliza-se a letra "I" no início do nome para especificar que será uma interface. No exemplo acima, a interface "IVehicle" é utilizada na class "Car" através do comando "implements".

Herança

Uma class também pode herdar outra class, como por exemplo, criando uma nova class Super Car que herda os tipos da class no exemplo anterior Car:

class SuperCar extends Car {
    engine;
    constructor(brand: string, wheels: number, engine: number) {
        super(brand, wheels);
        this.engine = engine;
    }
}

// criando uma nova class supercar com herança

const a4 = new SuperCar("Audi", 4, 2.0);

console.log(a4);
a4.showBrand();
Enter fullscreen mode Exit fullscreen mode

No exemplo acima utilizamos uma nova class que possui sua herança da class Car, porém nessa nova class adicionamos a nova chave "engine". Para se utilizar o constructor dentro da class herdada, devemos passar os tipos que declaramos no constructor "pai" e o novo declarado no constructor filho, e para atribuir os valores, utilizamos o "super" para extrair esses valores da class "pai".

Decorators

Os decorators são um recurso do typescript muito utilizados para validação de dados, para construir um evento observável em alguma class ou função. Um recurso complexo para quem é iniciante no typescript. Porém caso deseje utilizar, lembre-se de habilitar o mesmo dentro do seu arquivo de configuração, através da chave experimentalDecorators.

class Person {
    name;
    constructor(name: string) {
        this.name = name;
    }
}
Enter fullscreen mode Exit fullscreen mode

Temos a class Person que recebe a variável name quando o usuário é criado, porém se quiser adicionar um id e uma data de criação para esse usuário, podemos utilizar um constructor, uma vez que não é o usuário que preenche essas informações, ambas devem ser geradas pelo sistema.

Constructor são funções que retornam outras funções que contenham as informações que queremos alterar na class base.

function BaseParams() {
return function <T extends { new (...args: any[]): {} }>(constructor: T) {
    return class extends constructor {
    id = Math.random();
    createdAt = new Date();
    };
};

}

//Chamando o constructor
@BaseParams()

class Person {
    name;
    constructor(name: string) {
    this.name = name;
    }
}

const newUserMarlon = new Person("Marlon");
console.log(newUserMarlon);
Enter fullscreen mode Exit fullscreen mode

A função baseParams basicamente cria os parâmetros base para a criação de qualquer outra class, nesse caso contendo um id e uma data de criação.

Para trabalhar com construtores, devemos esperar qualquer tipo de argumento, pois como serão parâmetros base para as classes, teremos provavelmente classes diferentes umas das outras, portanto adotar uma abordagem mais genérica, tentando sempre evitar o any. Para isso utilizaremos a tag genérica.

Para utilizar um constructor devemos usar o "@" para exemplificar que estamos utilizando um constructor dentro do código.

Configurando o Projeto

Verificar se o typescript está instalado na máquina:

npx tsc -v
Enter fullscreen mode Exit fullscreen mode

Ou usando yarn:

yarn tsc -v
Enter fullscreen mode Exit fullscreen mode

Caso não tenha o typescript instalado na sua máquina, verifique a documentação para poder instalar de acordo com seu gerenciador de pacotes preferido.

Assim que tiver o arquivo instalado na máquina, pode prosseguir com o seguinte comando para iniciar um arquivo de configuração:

Certifique-se de já estar dentro do seu diretório

npx tsc --init 
Enter fullscreen mode Exit fullscreen mode

Arquivo de configuração criado, pode-se alterar esse arquivo tsconfig.json da seguinte maneira:

rootDir - Diretório root, deve ser alterado para onde ficará armazenado o index.ts da sua aplicação (No caso do projeto a pasta src);

outDir - Diretório de saída, deve ser apontado o diretório que será compilado o arquivo javascript, nesse caso dentro da pasta dist na pasta js

Para validar, crie seu "Hello World" no arquivo index.ts e para compilar rode no seu terminal o comando npx tsc, e certifique-se se a leitura do arquivo de entrada e saída estão de acordo.

target - O target define a linguagem javascript que será compilada, portanto certifique-se de utilizar a versão que mais se adapta ao seu projeto.

Ao final, sua estrutura deve ficar parecido com algo tipo isso:

├── dist (or build)
│   ├── js
│   │   ├── index.js
│   ├── index.html
├── src
│   ├── index.ts
└── tsconfig.json
Enter fullscreen mode Exit fullscreen mode

Compilando o Projeto

Para que o arquivo index.js seja observado pelo typescript para que qualquer alteração seja alterado novamente, podemos usar o comando:

npx tsc -w
Enter fullscreen mode Exit fullscreen mode

Se você utiliza o VScode como IDE, pode-se usar a extensão live-server para abrir um servidor para servir sua aplicação estática, e tendo acesso ao console poderá ver o javascript que está sendo gerado pelo typescript.

Top comments (0)