Como usar generics no TypeScript?
Tags: typescript, generics, react, javascript
Published: 2020-10-31 13:43:26

# Generics
Quando estamos escrevendo um código, uma das coisas que precisamos nós atentar é a reutilização dele, assim eu consigo escrever menos e ter apenas um ponto de contato caso tenha algum bug. Mas para fazer isso nós temos que tornar esses **métodos mais genéricos**. Quando nós trabalhamos com uma linguagem tipada igual ao TS nós sabemos que é preciso definir os tipos das nossas entradas no início e com isso o que era pra ser mais genérico acaba não sendo. É por isso que existem os *generics* que é para nós termos uma maior flexibilidade mesmo na linguagem tipada.

**Vamos começar fazendo uma alusão ao `useState` do React**

Nós vamos ver que podemos usar `numbers` e `string` através do `union` do TS. Então podemos começar com `string` e terminar com `number` ou vice-versa.

```typescript
function useState() { 
  let state: number | string;

  // Pega o estado atual
  function getState() {
    return state;
  }

  // Seta um novo estado
  function setState(newState: number | string) {
    state = newState;
  }

  return { getState, setState };
};

const newState = useState();

newState.setState(123);
console.log(newState.getState());

newState.setState('foo');
console.log(newState.getState());

```

**Agora nós vamos bloquear a alteração de tipo depois de definido**

Então, se eu defino que é uma `string` ele não pode alterar mais esse tipo. Quando agente cria uma função nós temos os `()` que é onde ficam os atributos da nossa função. Então, antes deles nós vamos usar os `generics`. Em geral para passar os tipos nós usamos certos símbolos, que são letras com significado semântico.

Vamos conferir a sintaxe:

```typescript 
function useState<S>() {
  let state: S;
}
```

Vemos que alteramos o `let state:` de `number | string` para `S`, mas o que ele significa?

- Esse símbolo representa para sua função que ela agora pode trabalhar com alguma coisa do tipo `S`. Na nossa primeira função passada acima, com esse código alterado ela ainda não vai mostrar erro, pois nós não falamos de que tipo é esse `S`.

Como definimos um tipo para `S`?

- Ela é feita diretamente no momento em que nós chamamos a função. Por padrão o tipo desses atributos são `unknow`, eles aceitam qualquer coisa, mas quando é passado para ele um tipo ele passa a ser só daquele tipo. E é isso que causa a flexibilidade do `generics`.

**Lembrando que temos alguma letras padrões:**

**S** - State
**T** - Type
**K** - Key
**V** - Value
**E** - Element

Feito alterações o código fica assim:

```typescript 
function useState<S>() { 
  let state: S;

  // Pega o estado atual
  function getState() {
    return state;
  }

  // Seta um novo estado
  function setState(newState: S) {
    state = newState;
  }

  return { getState, setState };
};

const newState = useState<string>(); // Atribuição de tipo para S.

newState.setState('123');
console.log(newState.getState());

newState.setState(123); // ERRO - Pois foi atribuído string ao unknow.
console.log(newState.getState());

newState.setState('123');
console.log(newState.getState());

```

Agora, seu eu passo que ele é um `number`, `boolean`, `any`, ele vai passar a ser todos.

**Mas isso gera um problema para nós, nós podemos alterar isso a qualquer momento!!!**

Para bloquear que ele seja o que nós passarmos para ele, nós temos que mudar isso:

```typescript
function useState<S extends number | string>() { 
  let state: S;
}
```

Aíiii, quando eu passar o primeiro tipo para ele, é esse que ele vai usar para sempre, 
então...

```typescript
function useState<S extends number | string>() { 
  let state: S;

  // Pega o estado atual
  function getState() {
    return state;
  }

  // Seta um novo estado
  function setState(newState: S) {
    state = newState;
  }

  return { getState, setState };
};

const newState = useState<number>(); // Pronto, agora ele só aceita number

newState.setState(123);
console.log(newState.getState());

newState.setState('123'); // AQUI VAI DAR ERRO!
console.log(newState.getState());

newState.setState(123);
console.log(newState.getState());

```

**FALADO**
"Eu sou flexível, posso ser qualquer coisa, mas depois que eu ganho um tipo, eu não mudo mais".

**Agora, vamos colocar um tipo como `default`.**

- É simples, é só fazer isso: 

```typescript 
// number é default
function useState<S extends number = number| string>() {} 

// Não preciso passar nada na chamada da função.
const newState = useState();
```

**Uso**
- Esses tipos são muitos usados com os `hooks` do React, mas podem ser usados com uma infinidade de coisas. Aleḿ disso, eles funcionam também com objetos maiores como: 
`React.FC<Props, States>`.

**Conclusão**

- Bom, é isso galera, espero poder ter ajudado, confesso que não é simples de entender mas é praticando que se aprender, então, vaaaleu, deixa eu ir praticar.