Uma introdução aos tipos avançados do TypeScript

Sempre que vemos código fonte de Bibliotecas com TypeScript vemos um monte de genericos, instanceof , keyof entre outros. Hoje venho dar um overview de todos eles.

TS

Por qual razão se usa TypeScript ?

A resposta parece obvia, por tipos é claro. Hoje quero escrever mais a fundo sobre os avançados do TypeScript. Antes de tudo vou deixar um link com uma referencia dentro do typescript sobre o seu sistema de tipos o Structural Subtyping o link é extremamente interessante para quem gostaria de ter um entendimento da base da linguagem.

Sem mais delongas eu começo falando de uma funcionalidade que combina muito com o DRY/ Funções Helpers, funções que são genericas que muitas vezes são reescritas apenas para fazer a mesma coisa com aquele tipo especifico.

Generics

Um exemplo basico de Generics é de criar funções que fazem coisas repetidas. Nesse caso para exemplificar eu diria uma função que envia algo para algum lugar e retorna o mesmo.

function fooToBar<foo, bar>(arg: Array<foo>): Array<bar> {
  // faz algo com foo e por fim faz bar, Geralmente vejo sendo usado com arrays uma vez que arrays possuem muitas funções que pode ser necessario reutilizar 
  const bar = makeBarUsingFoo(arg);
  return arg;
}

uso basico de genericos

O objetivo principal de generics é como seu proprio nome diz criar coisas genericas que VÃO te ajudar como funções helpers. Assim como em outras langs os genéricos criam esse desacomplamento de código da mesma forma que dão funcionalidade e expressividade. Funfact: Chamar o type de array de maneira generica possui semanticamente o mesmo resultado que sua versão curta: Array<Type> === Type[] .

keyof

Esse é um tópico que me lembra PHP vagamente pois Objetos em JavaScript e TypeScript possuem similaridade com Arrays Associativos em PHP.

obj.foo e obj['foo'] é a mesma forma de acessar um objeto, isso pode ser um problema caso a string de acesso esteja errada, para conter isso e criar ambientes dinamicos e bem interessantes, podemos usar a palavra reservada keyof.

type fooBar = { foo: boolean, bar: boolean };
let fbKey: keyof fooBar; // fbKey só pode assumir os valores de 'foo' ou 'bar'

// você pode acessar um objeto como o fooBar dessas maneiras
obj: fooBar;
const a = obj.foo // usando a notação de ponto
const z = obj['foo'] // aqui ele SÓ deixa os valores que são chaves do type  

exemplo sobre a palavra reservada keyof

in operator e o instanceof

Essa keyword quando usada com interfaces pode deixar ambientes dinamicos bem previsiveis o que torna o dia dia muito mais fácil e claro. Lembrando que funciona apenas com interfaces e classes, enquanto o in funciona em types. Para exemplificar eu gosto desse modelo:

class foo { fooMethod() {}}
class bar { barMethod() {}}
class bla { blaMethod() {}}
function doSomething(x: foo | bar | bla) {
  if (x instanceof foo) {
    x.fooMethod() // função que só tem em foo ou chamar função só aceita foo
  }
  if (x instanceof bar) {
    x.barMethod() // função que só tem em bar ou chamar função só aceita bar
  }
  if (x instanceof bla) {
    x.blaMethod() // função que só tem em bla ou chamar função só aceita bla
  }
}

// outra forma é usando o in caso 
type foo = { foo: string}
type bar = { bar: string}
function doSomething(x: foo | bar ) {
  if ("foo" in x) {
    // função que só tem em foo ou chamar função só aceita foo
    onlyFoo(x);
  }
  if ("bar" in x) {
    onlyBar(x) // função que só tem em bar ou chamar função só aceita bar
  }
}
function onlyFoo(p: foo){}
function onlyBar(p: bar){}

exemplo de in e instanceof

Ambos modelos podem ser uteis em criar roteamento em funções para lidar com dinamicidades do código.

Template Literals

Esse é um tópico que eu amo pois ajuda muito em relação a lidar com dados com nomes MUITO parecidos que podem ser de qualquer lugar.

A documentação do TypeScript nós dá um exemplo que eu considero maravilhoso.


type EmailLocaleIDs = "welcome_email" | "email_heading";
type Lang = "en" | "ja" | "pt";
 
type LocaleMessageIDs = `${Lang}_${EmailLocaleIDs}`; // esse type pode ser -> "en_welcome_email" | "en_email_heading" | "ja_welcome_email" | "ja_email_heading" | "pt_welcome_email" | "pt_email_heading"

``
_Exemplo de Template Literal_

Esse use case que a doc dá [aqui](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html) é surrealmente útil uma vez que dessa forma deixamos N formas de chamar algum valor em algum lugar pronto apenas por fazer essa únião de valores. Uma multiplicação relativamente simples mas que salva muito tempo. ao invés de escrever os 6 nomes que podem ser chamados, escreve-se apenas 5 nomes, parece pouca vantagem mas quando se tem 4 linguas o type nós auto-gera 8 possiveis types, caso tivesse 4 linguas e 5 emails temos mapeado 20 possibilidades, enquanto escrevemos apenas 9 variavéis.


## Conclusão
De maneira geral os tipos avançados em TypeScript são ou apenas usando alguma dessas funcionalidades que eu expliquei ou alguma combinação das mesmas para criar funcionalidades que vão ser uteis no futuro sem perder a expressividade que essa linguagem nós trás.

Contato

Se quiser discutir sobre qualquer assunto ou viu algum erro, não hesite em me marcar ou chamar no Twitter: @que_cara_legal
Estou sempre tentando trazer o que tenho estudado, as vezes traduzindo algums tópicos divertidos que me chamam atenção.