Tenha brio com sistemas de tipos

O sistema de tipos do TypeScript é muito complexo ao ponto de ser Turing Complete, ou seja podemos criar outros programas complexos com ele, hoje mostro qual a razão de aprendermos isso e alguns exemplos legais.

Brio

Brio ?

Brio é uma palavra sensacional e deveriamos utilizar ela mais vezes, eu só estudei o que eu estudei de sistema de tipos por causa do brio que eu tenho por esse tipo de conteúdo(parsers, compilers e afins)

Eu vi esses dias esse seguinte código e como uma pessoa com brio eu li até entender e hoje posso explicar nem que seja um pouco sobre sistemas de tipos

JSON parser

Gist desse código está aqui

O que é um sistema de tipos ?

Um sistema de tipos como a nossa wikipedia nos ajuda a dizer é:

é um conjunto de regras que atribuem uma propriedade chamada de tipo para as várias construções

Mas o que isso quer dizer no fim do dia ? É as regras que devemos seguir, como colocar pesos em nossas pernas igual do rock lee (sim, vai nós ajudar, confia)

Rocklee

Quando vamos trabalhar em code bases mais maduras com códigos que possuem tipos bem definidos, temos a certeza junto aos testes que esse código funciona como deveria. Tipos ajudam a fazer a análise estática do seu código.

Hoje iremos falar como brincar e torturar o sistema de tipos do TypeScript e de quebra aprender mais sobre como programar melhor e as bases no geral das linguagens de programação.

TypeScript

Antes de falar de código, vou comentar por cima onde está o typechecker e algumas curiosidades sobre esse módulo do sistema do TS.

O TypeChecker tem 2.67MB e fica nessa url e ele é capaz de algumas coisas sensacionais. As melhores imagens que podem explicar o que ele pode fazer são essas

TS Checker

Camadas

A imagem abaixo é um panorama de como o TypeScript funciona de baixo dos panos e é essencial para se saber o que eles está fazendo antes de gerar o código JS que é lido pelo navegador

camadas

Essas imagens veem desse repo da microsoft

Gists

Quem me segue no twitter sabe que eu faço muito código em type-level. Geralmente fica nos gists do github aqui Hoje vou analisar um que é relativamente simples mas muito bonito, que é um parser de HTML que eu implantei e se fosse fazer a nível de código normal ele possivelmente ficará muito parecido.

Ele segue abaixo e a partir dai irei comentar como que se cria código em TypeLevel

HTML parser

Parece muito código mas brio e com calma podemos chegar em algum lugar, em palavras humanas o que esse código faz ?

  Text extends `${infer L}${infer R}`  

Pegue Text e divida em 2 pedaços L e R, na primeira iteração da uma palavra como Charizard L será C e R será harizard, e assim por diante, funciona em arrays também.

Extends dessa maneira precisa de um ? e de um : caso o valor seja truthy ele ria continuar o fluxo, caso não quer dizer que acabou o objeto que você está tentando dividir. Você tem que ter um fallback quando isso acontece.

Extends ?

A palavra extends no sistema de tipos é usada para além de criar variáveis é usada como sinônimo de verificador de igualdade. No código acima usamos ele dessa forma:

 L extends '<' // Em ts normal poderia ser L === '<' ? ... : ... ou apenas if (L === '<') {...}

Até agora já percebemos algumas coisas, o sistema de tipos é focado em funções puras, ou seja, eu só trabalho com o que eu recebo de inputs e nada de side effects.

Além de podermos fazer if's, criar variáveis, precisamos de loops para podermos criar qualquer coisa na linguagem.

Temos isso e esse é o ponto onde eu gosto de pensar que nós como programadores preguiçosos que somos geralmente esquecemos.

Recursão

A maneira mais simples de se iterar algo, é por recursão. No sistemas de tipos podemos conseguir isso através de chamar o type de novo ao invés de retornar ele. no exemplo acima eu fiz isso todas as vezes e criei side effects apenas na hora de chamar a recursão de novo.

// Você passa texto simples aqui e ele irá só parar quando ver outro <

type ContentParser
  <Text extends string, Result extends string = ''> =
  Text extends `${infer L}${infer R}`
    ? L extends '<'
      ? Result
      : ContentParser<R, `${Result}${L}` >
    : never

A pergunta é como que ele cria esse texto que ele retorna, toda vez que eu itero pelo tipo eu vou concatenando o texto de Result até chegar em um momento que ele não precisa mais e eu retorno ele.

Consigo obter o mesmo resultado com essa função abaixo:

type ContentParser
  <Text extends string, Result extends string = ''> =
  Text extends `${infer L}${infer R}`
    ? L extends '<'
      ? Result
      : ContentParser<R, `${Result}${L}` >
    : never

const biggerThanRemover = (text: string, res: string = ''): string => {
  const [l, ...r] = text.split('');
  if (l === '<') {
    return res;
  }
  return biggerThanRemover(r.join(''), `${res}${l}`);
}
const charizard: ContentParser<'charizard<'> = biggerThanRemover('charizard<') as  'charizard'
console.log(charizard) // charizard

Sim esse simples código é que precisa para se criar um loop e eu particularmente acho muito mais elegando que um for loop clássico, a melhor parte é que é uma função pura, ou seja, muito fácil de ser testada e usada.

Isso me faz um programador melhor ?

Sim, ter mais armas em seu arsenal irá te fazer mais capaz de lidar com situações adversas e poderá a partir dai dizer o que vale ou não a pena em certos cénarios.

Saber fazer um loop com recursão é algo que eu hoje digo que aprendi com total certeza depois de brincar bastante com o sistema de tipos e sem esse tempo não seria possível.

Aprender algo que mesmo que por brincadeira e brio irá te ajudar a aprender coisas e chegar em certos limites que antes não eram realizáveis.

Conclusão

Por fim deixo um exemplo prático que aprendi, fazer a seus colegas devs terem a melhor devXP(famosa devUX) é essencial, com mensagens de erro personalizadas conforme o erro dos tipos pode ser algo que venha a ajudar seus colegas a ficarem mais produtivos.

Segue abaixo um código que eu considero ser o mais puro ouro que se pode encontrar pela internet link do post que eu achei:

custom error

Além de ele dar sintax highlight de erro, você pode argumentar o que está de errado com um texto mais explicativo que o simples x is not assignable to type y.

Aproveite e faça bom uso do mesmo :D

Referências

Gostaria de adicionar algumas referências que fazem útil ou que são legais de conhecer:

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.