Compreendendo Generic em TypeScript
Tags: typescript
Published: 2023-05-05 12:04:15


![typescript generics](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c9s5ijzsxlf246q9su59.png)

Olá pessoas! Bora entender/aprender sobre "genéricos" no **TypeScript**? Se você estiver querendo entender melhor, você veio ao lugar certo. Neste artigo, exploraremos o conceito de genéricos no **TypeScript** e forneceremos exemplos de código para ajudá-lo a entender melhor o tópico.

## Mas afinal o que é uma **Generic** em **TypeScript**?

De acordo com a própria documentação, os genéricos são modelos de código que você pode definir e reutilizar em toda a base de código. Eles fornecem uma forma de informar a funções, classes ou interfaces que tipo você deseja usar ao chamá-las.

Em termos simples, os genéricos no **TypeScript** permitem que você crie um código flexível e reutilizável que pode funcionar com uma variedade de tipos de dados. Essencialmente, eles permitem definir funções, classes e interfaces que podem funcionar com qualquer tipo de dados, em vez de serem limitados a um tipo específico.

Usando um tipo genérico, você pode criar funções e classes que podem trabalhar com diferentes tipos de dados de maneira flexível e segura. Por exemplo, imagine que você precise criar uma função que calcule a soma de dois números. Em vez de definir os tipos de entrada como números, você pode usar um tipo genérico para criar uma função que aceite qualquer tipo de dados que possam ser somados.

## Legal, mas como usar tipos genéricos em TypeScript?

Vamos dar uma olhada em alguns exemplos de código simples para ver como os genéricos funcionam no TypeScript. Primeiro, vamos criar uma função simples que analisa se dois números são iguais, retornando um booleano como resultado:

```typescript
function isEqual(a: number, b: number): boolean {
  return a === b;
}

console.log(isEqual(2, 3)); // output: true
```

Note que no exemplo acima, a função `isEqual` só irá funcionar, da forma correta, se os dois argumentos passados forem do tipo `number`.

Caso a gente tente passar qualquer outro tipo para a função `isEqual`, por exemplo um string, iremos ser atingidos pelo error:

```typescript
console.log(add("2", "3")); // error: Argument of type '"2"' is not assignable to parameter of type 'number'.
```

Agora você deve pensar: _"Pow super simples de solucionar, taca um `any` na tipagem e está tudo resolvido" _

Vamos seguir essa proposta no exemplo abaixo:

```typescript
function isEqual(a: any, b: any): boolean {
  return a === b;
}

console.log(isEqual(2, 3)); // output: false
```

Você deve acreditar que isso resolva seu problema, porém com isso criamos mais problemas ainda. Não é bom  usar `any` em suas **tipagens** (isso daria um ótimo artigo não???), por hora, tudo que você precisa saber é que deve ser evitado em funções porque isso pode levar a problemas de segurança de tipo em tempo de execução e dificuldades na manutenção do código. A falta de especificidade em relação aos tipos de dados pode prejudicar a documentação do código e também a integração com outras bibliotecas e frameworks que exigem tipos específicos. Em resumo, é importante evitar o uso de `any` em funções no **TypeScript** para manter a segurança de tipo e a facilidade de manutenção do código.

Exemplo do que pode ocorrer com sua função:

```typescript
function isEqual(a: any, b: any): boolean {
  return a === b;
}

console.log(isEqual(2, "2")); // output: false
```

Note que estamos enviando um número e uma string, e nossa verificação analisa não somente se são iguais, mas se são do mesmo tipo, no caso sempre será um resultado `false`.


## Então não posso usar `any`, o que vou usar?

Calma meu pequeno gafanhoto, é aqui que o genérico em **TypeScript** brilha. 

A sintaxe para definir um tipo genérico em **TypeScript** é bastante simples. Você pode definir um tipo genérico adicionando um par de colchetes angulares `<>` após o nome do tipo ou da função, seguido por um identificador que será usado para se referir ao tipo genérico.

Por exemplo, aqui está nossa função simples que usa um tipo genérico para verificar se dois números são iguais:

```typescript
function isEqual<T>(a: T, b: T): T {
  return a === b;
}
```

Agora usamos a sintaxe `<T>` para indicar que a função `isEqual` pode funcionar com qualquer tipo de dados. O `T` é apenas um espaço reservado que pode ser substituído por qualquer tipo de dado. Também alteramos o tipo de retorno para `T`, para que a função retorne o mesmo tipo de dado que foi passado.

Com isso caso a gente tente passar tipos diferentes o typescript ira nos alertar da seguinte maneira:

```typescript
console.log(add("Hello, ", 123)); // error: Argument of type 'number' is not assignable to parameter of type 'string'.
```

Legal não é mesmo? 

## Usando Generic em classes

Agora que vimos como usar genéricos com funções, vamos dar uma olhada em como eles podem ser usados com classes. Bora para um exemplo bem simples de uma classe `List` simples:

```typescript
class List {
  private data: any[] = [];

  push(item: any) {
    this.data.push(item);
  }

  pop() {
    return this.data.pop();
  }
}
```

Essa classe `List` pode funcionar com qualquer tipo de dados, mas não é muito segura. Podemos acidentalmente colocar uma string na lista e, em seguida, tentar retirar um número, causando erros em tempo de execução.

Agora vamos reescrever a classe `List` usando genéricos:

```typescript 
class List<T> {
  private data: T[] = [];

  push(item: T) {
    this.data.push(item);
  }

  pop(): T | undefined {
    return this.data.pop();
  }
}
```

Note que usamos a sintaxe `<T>` para indicar que a classe `List` pode funcionar com qualquer tipo de dados. Também alteramos a propriedade `data` para ser uma matriz de elementos `T` e atualizamos os métodos `push` e `pop` para aceitar e retornar valores `T`. Também adicionamos um tipo de retorno de `T | undefined` ao método `pop`, indicando que ele pode retornar `undefined` se a lista estiver vazia.

Agora, ao criarmos uma nova instância do List, podemos especificar o tipo de dado com o qual ela irá trabalhar:

```typescript
const numberList = new List<number>();
numberList.push(1);
numberList.push(2);
console.log(numberList.pop()); // output: 2

const stringList = new List<string>();
stringList.push("hello");
stringList.push("world");
console.log(stringList.pop()); // output: "world"
```

Ao usar genéricos, tornamos nossa classe List mais segura e flexível.

## E como funciona, caso eu queria usar em interfaces? 

Legal, vamos a mais um exemplo, agora vamos dar uma olhada em como os genéricos podem ser usados com interfaces.  iremos criar uma interface que define um `Dictionary`:

```typescript
interface Dictionary {
  [key: string]: any;
}
```

Note que nossa interface `Dictionary` pode ser usada para representar um objeto que possui chaves de string e qualquer tipo de valor (olha o any ai de novo gente, vou escrever um artigo do porque não usar prometo). Mas e se quisermos criar um `Dictionary` que tenha chaves de um tipo específico e valores de um tipo específico?

Xiii ai complicou né? Calma pequeno gafanhoto, vamos reescrever a interface do Dictionary usando genéricos:

```typescript
interface Dictionary<T> {
  [key: string]: T;
}
```

Caracas, notaram como usamos a sintaxe <T> para indicar que a interface Dictionary pode funcionar com qualquer tipo de dados? E Também alteramos a assinatura do índice para usar `T` como o tipo para os valores?

Ao criarmos uma nova instância do `Dictionary`, podemos especificar o tipo de dado com o qual ela irá trabalhar, pega só o exemplo:

```typescript
const numberDict: Dictionary<number> = { one: 1, two: 2 };
console.log(numberDict.one); // output: 1

const stringDict: Dictionary<string> = { hello: "world", foo: "bar" };
console.log(stringDict.hello); // output: "world"
```

Ao usar genéricos com interfaces, criamos uma maneira mais segura e flexível de definir nossas estruturas de dados, tornamos nosso código mais flexível e seguro em termos de tipo.

## Bora de resumo

Os tipos genéricos são uma poderosa ferramenta em `TypeScript` que permitem criar funções e classes flexíveis e reutilizáveis que podem trabalhar com diferentes tipos de dados. Usando tipos genéricos, você pode tornar seu código mais genérico e flexível, além de aumentar a segurança do seu código. 

É meu pequeno gafanhoto, espero que esse artigo tenha ajudado você a entender o conceito de tipos genéricos em TypeScript e como usá-los em seu código.

Se você tenha interesse em aprender mais sobre TypeScript, há muitos recursos disponíveis online, incluindo a documentação oficial do TypeScript. Com algum esforço e prática, você poderá usar tipos genéricos e outras funcionalidades do TypeScript para criar aplicativos mais seguros, eficientes e escaláveis.

[Link documentação oficial](https://www.typescriptlang.org/docs/handbook/2/generics.html)
[Link curso da microsoft](https://learn.microsoft.com/pt-br/training/modules/typescript-generics/)