Tipos Utilitários em TypeScript

Tipos Utilitários em TypeScript

Este artigo explica os tipos utilitários em TypeScript.

YouTube Video

Tipos Utilitários em TypeScript

Os tipos utilitários do TypeScript são ferramentas convenientes para criar novos tipos com base em tipos existentes. Isso permite definições de tipos mais flexíveis e aumenta a reutilização do código. Aqui, explicaremos em detalhe os tipos utilitários mais utilizados e discutiremos como usá-los com exemplos de código.

Partial<T>

Partial<T> torna todas as propriedades de um tipo de objeto opcionais (permitindo undefined). É útil quando você quer usar apenas algumas das propriedades que o tipo original possui.

 1interface User {
 2  id: number;
 3  name: string;
 4  age: number;
 5}
 6
 7function updateUser(user: Partial<User>) {
 8  console.log(user);
 9}
10
11updateUser({ name: "Alice" }); // Updates only 'name'

Explicação:

No exemplo acima, usar Partial<User> torna todas as propriedades do tipo User opcionais. Portanto, na função updateUser, você pode passar apenas um subconjunto das propriedades.

Required<T>

Required<T> torna todas as propriedades, inclusive as opcionais, obrigatórias. É usado quando você quer converter propriedades opcionais para serem obrigatórias.

 1interface User {
 2  id: number;
 3  name?: string;
 4  age?: number;
 5}
 6
 7function createUser(user: Required<User>) {
 8  console.log(user);
 9}
10
11// createUser({ id: 1 }); // Error: 'name' and 'age' are required
12createUser({ id: 1, name: "Alice", age: 25 });

Explicação:

Ao usar Required<User>, propriedades como name e age são tratadas como obrigatórias.

Readonly<T>

Readonly<T> torna todas as propriedades de um objeto somente leitura. Isso impede que os valores do objeto sejam alterados.

 1interface User {
 2  id: number;
 3  name: string;
 4}
 5
 6const user: Readonly<User> = {
 7  id: 1,
 8  name: "Alice"
 9};
10
11// user.id = 2; // Error: 'id' is read-only
12console.log(user);

Explicação:

Ao usar Readonly<T>, você pode proteger as propriedades de um objeto contra alterações. É eficaz quando você deseja evitar que dados sejam acidentalmente modificados durante o desenvolvimento.

Record<K, T>

Record<K, T> cria um tipo de mapa com tipos de chave e valor especificados. K é o tipo para as chaves (como string ou number), e T é o tipo para os valores.

 1type Roles = "admin" | "user" | "guest";
 2interface Permissions {
 3  read: boolean;
 4  write: boolean;
 5}
 6
 7const rolePermissions: Record<Roles, Permissions> = {
 8  admin: { read: true, write: true },
 9  user: { read: true, write: false },
10  guest: { read: false, write: false },
11};
12
13console.log(rolePermissions);

Explicação:

Record<K, T> é útil quando você quer definir pares de chave-valor. No exemplo acima, permissões são definidas com base em funções de usuário.

Pick<T, K>

Pick<T, K> extrai apenas propriedades especificadas de um tipo de objeto. Você pode criar um novo tipo extraindo apenas as propriedades necessárias.

 1interface User {
 2  id: number;
 3  name: string;
 4  age: number;
 5}
 6
 7type UserSummary = Pick<User, "id" | "name">;
 8
 9const summary: UserSummary = {
10  id: 1,
11  name: "Alice"
12};
13
14console.log(summary);

Explicação:

Ao usar Pick<T, K>, você pode extrair propriedades específicas de um tipo de objeto e tratá-las como um novo tipo. Por exemplo, apenas id e name são extraídos no exemplo acima.

Omit<T, K>

Omit<T, K> exclui propriedades especificadas de um tipo de objeto. Esta é a operação oposta a Pick.

 1interface User {
 2  id: number;
 3  name: string;
 4  age: number;
 5  email: string;
 6}
 7
 8type UserWithoutEmail = Omit<User, "email">;
 9
10const userWithoutEmail: UserWithoutEmail = {
11  id: 1,
12  name: "Alice",
13  age: 25
14};
15
16console.log(userWithoutEmail);

Explicação:

Ao usar Omit<T, K>, você pode criar um novo tipo excluindo propriedades especificadas. No exemplo acima, a propriedade email é excluída.

Exclude<T, U>

Exclude<T, U> cria um novo tipo removendo o tipo U do tipo união T. É usado quando você deseja remover um tipo específico.

1type Status = "active" | "inactive" | "pending";
2type ExcludedStatus = Exclude<Status, "pending">;
3
4const status: ExcludedStatus = "active"; // "pending" is excluded, so it cannot be chosen
5console.log(status);

Explicação:

Ao usar Exclude<T, U>, você pode remover tipos desnecessários dentro de um tipo união. No exemplo acima, como "pending" é excluído, apenas "active" ou "inactive" podem ser selecionados.

Extract<T, U>

Extract<T, U> extrai as partes que correspondem ao tipo U dentro do tipo união T. É útil quando você deseja extrair apenas um tipo específico.

1type Status = "active" | "inactive" | "pending";
2type ActiveStatus = Extract<Status, "active" | "pending">;
3
4const status: ActiveStatus = "active"; // "inactive" cannot be chosen
5console.log(status);

Explicação:

Extract<T, U> realiza a operação oposta a Exclude. No exemplo acima, apenas "active" e "pending" podem ser selecionados.

NonNullable<T>

NonNullable<T> cria um tipo com null e undefined excluídos. É útil quando você deseja excluir esses valores de tipos opcionais.

1type UserName = string | null | undefined;
2type ValidUserName = NonNullable<UserName>;
3
4const name: ValidUserName = "Alice"; // null and undefined cannot be chosen
5console.log(name);

Explicação:

NonNullable<T> exclui null e undefined de um tipo que os contém. Isso é útil quando você deseja garantir que um valor definitivamente exista.

Conclusão

Os tipos utilitários do TypeScript são ferramentas poderosas para tornar as definições de tipo mais concisas e flexíveis. Compreender e usar adequadamente os tipos utilitários básicos como Partial, Required, Readonly, Record, etc., pode melhorar a reutilização e a manutenção do código. Dominar esses tipos permite definições de tipo mais robustas e seguras, possibilitando um desenvolvimento eficiente.

O operador keyof do TypeScript

O operador keyof do TypeScript é usado para recuperar todos os nomes de propriedades de um tipo de objeto. Usando este operador, você pode obter as chaves de um tipo de objeto como um tipo união. Isso é extremamente útil para escrever código com tipagem segura.

Uso Básico

1interface Person {
2    name: string;
3    age: number;
4    email: string;
5}
6
7// Use keyof to get the property names of Person
8type PersonKeys = keyof Person; // "name" | "age" | "email"

Exemplo de Uso

  1. Usando-o em Argumentos de Função

    Você pode usar keyof para definir uma função que tenha tipos baseados em propriedades específicas de um objeto.

1function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
2    return obj[key];
3}
4
5const person: Person = { name: "Alice", age: 30, email: "alice@example.com" };
6const name = getProperty(person, "name"); // type is string
7const age = getProperty(person, "age");   // type is number
  1. Aprimorando Restrições de Tipo

    Usando keyof, você pode garantir que as chaves passadas para uma função sejam validadas em tempo de compilação.

1// Passing an invalid property name results in an error
2const invalid = getProperty(person, "invalidKey"); // Error

Resumo

  • O operador keyof é usado para recuperar todos os nomes de propriedades de um tipo de objeto.
  • Você pode obter os nomes das propriedades como um tipo união, alcançando um código com tipagem segura.
  • Usando-o em argumentos de função, você pode restringir a função a aceitar apenas nomes de propriedades válidos.

Dessa forma, o operador keyof aprimora a segurança de tipos no TypeScript e ajuda você a escrever um código mais robusto.

Você pode acompanhar o artigo acima usando o Visual Studio Code em nosso canal do YouTube. Por favor, confira também o canal do YouTube.

YouTube Video