Tipos avançados de TypeScript explicados com exemplos

O TypeScript oferece vários tipos avançados que vão além dos tipos básicos, permitindo sistemas de tipos mais flexíveis e poderosos. Esses tipos avançados ajudam na criação de aplicativos robustos, fornecendo maneiras adicionais de definir e impor restrições de tipo. Este artigo explora alguns desses tipos avançados com exemplos.

Tipos de União

Tipos de união permitem que uma variável seja um de vários tipos. Isso pode ser útil quando um valor pode ser de vários tipos, mas deve ser tratado apropriadamente com base em seu tipo real.

// Union type example

function formatValue(value: string | number): string {
  if (typeof value === 'string') {
    return `String: ${value}`;
  } else {
    return `Number: ${value.toFixed(2)}`;
  }
}

console.log(formatValue("Hello"));
console.log(formatValue(123.456));

Neste exemplo, a função `formatValue` aceita uma string ou um número e formata o valor adequadamente.

Tipos de intersecção

Tipos de interseção combinam vários tipos em um. Um objeto de um tipo de interseção terá todas as propriedades dos tipos combinados. Isso é útil para compor vários tipos juntos.

// Intersection type example

interface Person {
  name: string;
  age: number;
}

interface Contact {
  email: string;
  phone: string;
}

type Employee = Person & Contact;

const employee: Employee = {
  name: "John Doe",
  age: 30,
  email: "[email protected]",
  phone: "123-456-7890"
};

console.log(employee);

Aqui, o tipo `Funcionário` é uma interseção de `Pessoa` e `Contato`, o que significa que contém propriedades de ambas as interfaces.

Tipos Literais

Tipos literais especificam valores exatos que uma variável pode conter. Isso pode ser particularmente útil para garantir que apenas certos valores específicos sejam permitidos.

// Literal type example

type Direction = "up" | "down" | "left" | "right";

function move(direction: Direction): void {
  console.log(`Moving ${direction}`);
}

move("up");    // Valid
move("down");  // Valid
// move("side"); // Error: Argument of type '"side"' is not assignable to parameter of type 'Direction'.

O tipo `Direction` aqui é restrito a quatro valores de string específicos, garantindo que somente essas direções possam ser usadas na função `move`.

Tipos de Tupla

Tipos de tupla representam um array com um número fixo de elementos onde cada elemento pode ter um tipo diferente. Tuplas são úteis para representar coleções de tamanho fixo de itens heterogêneos.

// Tuple type example

let user: [string, number] = ["Alice", 30];

console.log(user[0]); // "Alice"
console.log(user[1]); // 30

// user = [30, "Alice"]; // Error: Type 'number' is not assignable to type 'string'.

A tupla `user` é definida com uma string seguida por um número, e essa estrutura deve ser mantida.

Tipos condicionais

Tipos condicionais permitem que tipos sejam determinados com base em condições. Eles fornecem uma maneira de selecionar um tipo ou outro com base em uma condição.

// Conditional type example

type IsString = T extends string ? "Yes" : "No";

type Test1 = IsString;  // "Yes"
type Test2 = IsString;  // "No"

Neste exemplo, o tipo `IsString` verifica se um tipo `T` é uma string. Ele retorna `"Yes"` se for e `"No"` caso contrário.

Tipos mapeados

Tipos mapeados permitem criar novos tipos transformando propriedades de um tipo existente. Isso é útil para modificar ou estender tipos existentes.

// Mapped type example

type ReadonlyPerson = {
  readonly [K in keyof Person]: Person[K];
};

const readonlyPerson: ReadonlyPerson = {
  name: "Alice",
  age: 30
};

// readonlyPerson.age = 31; // Error: Cannot assign to 'age' because it is a read-only property.

O tipo `ReadonlyPerson` transforma o tipo `Person` tornando todas as suas propriedades somente leitura.

Conclusão

Os tipos avançados do TypeScript fornecem ferramentas poderosas para definir e gerenciar requisitos de tipos complexos. Ao utilizar tipos union, intersection, literal, tuple, conditional e mapped, os desenvolvedores podem criar aplicativos mais robustos e sustentáveis. Entender e aplicar esses tipos efetivamente pode aumentar significativamente a segurança e a flexibilidade do código TypeScript.