Recentemente, vi um tweet de Jamie Kyle sobre o uso de desestruturação, parâmetros padrão e tipos embutidos:
Esse tweet e alguns componentes do React que vi recentemente em meu trabalho diário me inspiraram a escrever esta postagem no blog. Quero mostrar a você como usar tipos de desestruturação e embutidos pode tornar seu TypeScript menos legível!
Em JavaScript e TypeScript, você pode definir uma função usando a palavra-chave function
ou a função lambda/seta. Ambas as formas são válidas, mas têm suas diferenças. Vamos dar uma olhada na função simples sendMessage
. A lógica de implementação não é relevante para nós.
// sendMessage function written using `function` keyword function sendMessage(message: string) { // function logic } // same sendMessage written as arrow function const sendMessage = (message: string) => { // function logic };
Quando a definição da função é bastante simples, a função aceita alguns parâmetros de um tipo diferente. Se forem primitivos como strings ou números, tudo é legível.
Digamos que você queira passar algumas informações adicionais junto com o conteúdo de sua mensagem para a função sendMessage
.
function sendMessage(message: { content: string; senderId: string; replyTo?: string; }) { // you can assess content using `message.content` here }
Como você pode ver, o TypeScript permite que você escreva uma definição de tipo embutida para o objeto de message
que deseja passar sem especificar o tipo usando a palavra-chave type
ou interface
.
Vamos adicionar Destructuring. Quando você passa um grande objeto de message
para sua função, o TypeScript permite separar os argumentos passados para reduzir o padrão de código da variável de message
repetida muitas vezes.
function sendMessage({ content, senderId, replyTo, }: { content: string; senderId: string; replyTo?: string; }) { // you have access to `content` directly }
Pode parecer uma boa ideia, afinal não é preciso escrever message
vezes, certo? Acontece que não é tão bom. Vamos falar sobre 5 razões pelas quais eu acho que é um antipadrão.
Ao ler o corpo da função, você vê senderId
e precisa verificar novamente para ter certeza de onde essa função vem. É passado como um argumento ou calculado em algum lugar da função?
Não há lugar natural para escrever comentários de documentação quando todos os tipos são limitados pela desestruturação na definição da função. Você pode escrever comentários entre cada campo de tipo, mas isso torna toda a definição da função ainda mais longa. Está ativamente desencorajando você a escrever um resumo rápido dos dados que está passando.
Quando seus dados são desestruturados, você precisa estruturá-los novamente em um novo objeto se quiser passá-los adiante. Isso desencoraja a criação de funções auxiliares menores e a dependência da composição para construir a lógica da função principal.
Se você precisar reutilizar seus argumentos de função em funções auxiliares ao compor sua lógica de função principal, deverá digitar o mesmo conjunto de tipos repetidamente. Isso torna mais fácil não escrever tipos.
Vamos encarar. São apenas muitas linhas de código que ocupam muito espaço na tela. Além disso, ele se concentra nos detalhes da implementação – o tipo interno dos argumentos que você está passando para uma função, que na maioria das vezes não é relevante quando você está olhando para essa função.
Extrair o tipo e colocá-lo logo acima da função o torna muito mais legível. Há um local para comentários de documentação, você pode reutilizar esse tipo em alguma outra função auxiliar e alterar a definição de tipo em um local, se necessário.
/** * Message to send using XYZ API */ export type MessageToSend = { /** * Markdown string of the user's message */ content: string; /** * Id of the sender user */ senderId: string; /** * Other message ID if this is a reply */ replyTo?: string; }; function sendMessage(message: MessageToSend) { // function logic } function getUserIdsToNotify(message: MessaageToSend) { // function logic }
Encontre uma lista de recursos que usei ao pesquisar esta postagem no blog: