DEV Community

Andres dos Santos
Andres dos Santos

Posted on

Como usar generics no TypeScript?

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.

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());

Enter fullscreen mode Exit fullscreen mode

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:

function useState<S>() {
  let state: S;
}
Enter fullscreen mode Exit fullscreen mode

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:

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());

Enter fullscreen mode Exit fullscreen mode

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:

function useState<S extends number | string>() { 
  let state: S;
}
Enter fullscreen mode Exit fullscreen mode

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

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());

Enter fullscreen mode Exit fullscreen mode

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:
// number é default
function useState<S extends number = number| string>() {} 

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

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.

Discussion (0)