TypeScript: 18 dicas e padrões para seu código!

Uma lista completa para utilizar com ou sem React

Image for post
Image for post
"Melhorando a qualidade do seu JavaScript assim ó.."

⭐️ Créditos

🚨 Esse artigo usa as seguintes versões de bibliotecas:

{
"@types/react": "16.4.16",
"@types/react-dom": "16.0.9",
"typescript": "3.1.3",
"react": "16.5.2",
"react-dom": "16.5.2"
}

📚 O código fonte pode ser encontrado no meu perfil GitHub

O TypeScript é definitivamente a melhor coisa que aconteceu com o JavaScript. Ponto.

Infelizmente, não posso dizer o mesmo sobre “É a melhor coisa que aconteceu para desenvolvedores Java/C# escreverem JavaScript”.

Tabela de conteúdo

  1. Acessadores private dentro de classes
  2. Acessadores protected dentro de classes
  3. Evite usar enum
  4. O método constructor em classes
  5. Evite decoradores para suas classes
  6. Faça a busca de tipos (state/props) diretamente no componente
  7. Sempre forneça tipos explícitos para props children
  8. Use inferência de tipos para definir state de componentes ou defaultProps
  9. Ao usar uma função como fábrica ao invés de classes para modelos/entidades, aproveite a fusão de declaração, exportando o tipo e a implementação
  10. Use a importação padrão para importar React
  11. Evite namespace
  12. Evite importações de módulos ES2015 ao importar tipos que não tenham código de tempo de execução
  13. Evite camelCase/PascalCase para nomes de arquivos
  14. Declare tipos antes da implementação
  15. Evite declarações de métodos dentro de alias de interface/tipo
  16. Evite number para chave de tipo indexável
  17. Evite JSX.Element para anotar o tipo de retorno de função/componente ou filhos/props

Porquê?

Eu sempre estive tentando ficar longe de vários recursos do TypeScript (por boas razões, explicadas neste artigo) para permanecer na área padrão/idiomático do JavaScript, o máximo possível.

Este artigo descreve vários padrões/dicas que eu “inventei/aprendi” e tenho usado no TypeScript e no React para construir interfaces de usuário.

Nota: Inicialmente, este post tinha “apenas” 10 dicas, durante a revisão, eu já adicionei mais 8 💪. Eu posso adicionar outros no futuro, conforme os padrões React/TypeScript mudarem/melhorarem/evoluirem. Certifique-se de verificar esse post periodicamente para atualizações.

Todo o artigo foi escrito como um “guia de estilo” com 3 subseções para cada dica/padrão que consiste em:

  • “⚠️ Não”, exemplos de código que você não deveria estar fazendo
  • “✅ Bom”, “⭐️ Melhor”, “💡 Considerar”, exemplos de código que você deveria estar fazendo
  • “❓Por quê”, explicação e ou raciocínio por trás da dica/padrão

Ficou claro? Agora vamos pular para as 10+ dicas em TypeScript para utilizar com ou sem React.

1. Acessadores public dentro de classes

Image for post

✅ Bom:

Image for post

❓Por quê:

Todos os membros dentro da classe são públicos por padrão (e sempre públicos em tempo de execução, o private ou protected do TS irá "ocultar" propriedades/métodos específicos da classe somente durante o tempo de compilação). Não introduza complexidade desnecessária na sua base de código. O acessador public não é um "JavaScript válido/idiomático".

2. Acessadores private dentro de classes

Image for post

✅ Bom:

Image for post

⭐️ Melhor:

Image for post

❓Por quê:

O acessador private não tornará suas propriedades/métodos na classe privada durante o tempo de execução. É apenas "emulação" que o TypeScript cria durante o tempo de compilação. Não se deixe enganar e tornar as coisas "privadas" usando padrões bem conhecidos como:

Na realidade, você quase nunca precisa trabalhar diretamente com a instância de um componente React e nem acessar suas propriedades de classe.

3. Acessadores protected dentro de classes

Image for post

✅ Bom:

Image for post

❓Por quê:

Utilizar protected é um alerta vermelho imediato em termos de padrões funcionais aproveitados pelo React. Existem padrões mais efetivos do que esse para estender o comportamento de algum componente. Você pode usar:

  • extrair a lógica do componente (como visto acima)
  • HOC (high order function ou função de alta ordem) e composição funcional
  • ‌CaaF (children as a function, a prop children como uma função)

4. Evite usar enum

Image for post

✅ Bom:

Se você precisar dar suporte a enums em tempo de execução, use o seguinte padrão:

Image for post

⭐️ Melhor:

Se você não precisa suportar enums de tempo de execução, tudo o que você precisa usar são literais de tipo:

Image for post

❓Por quê:

Usar enum em TypeScript pode ser muito tentador, especialmente se você estiver vindo de uma linguagem como C# ou Java. Mas há maneiras melhores de interpretar ambos com padrões idiomáticos bem conhecidos de JS ou como pode ser visto no exemplo "⭐️ Melhor", utilizando literais de tipo em tempo de compilação.

  • O resultado compilado de enums gera um código desnecessário (que pode ser mitigado com const enum, e enums de string são melhores neste caso)
Image for post
  • enums que não são de string não são compilados a um modo literal adequado e podem introduzir um bug não tratado em seu aplicativo
Image for post

🙇‍ Utilitário para enum

Em nosso exemplo “✅ Bom”, você pode pensar como, “ugh é um monte de boilerplate cara!”. Eu escutei meus amigos. Alto e claro 🙏

Se você precisar dar suporte a enums em tempo de execução, por quaisquer motivos, você pode aproveitar a pequena função utilitária da biblioteca de rex-tils como mostrada aqui:

Image for post

5. O método constructor em classes

Image for post

✅ Bom:

Image for post

❓Por quê:

Não há necessidade de usar o construtor em componentes React.

Se você fizer isso, você precisa fornecer mais boilerplate e também precisa chamar super com as props fornecidas (se você esquecer de passar as props para o super, seu componente conterá bugs, pois as props não serão propagadas corretamente).

Mas… mas… ei! Na documentação oficial do React eles usam construtor!

Tudo bem, a equipe do React usa a versão atual do JS para mostrar as coisas.

‌Mas… mas…, as propriedades de classe não são JavaScript padrão!

Os campos de classe estão no estágio 3, o que significa que eles serão implementados no JS em breve

Inicializando estado com alguma lógica

Basta definir uma função pura, fora do componente, com sua lógica personalizada (como um “efeito colateral”, você também obterá um código facilmente testado).

Image for post

6. Evite decoradores para suas classes

Image for post

✅ Bom:

Image for post

⭐️ Melhor:

Image for post

❓Por quê:

Decoradores são parasitas 🐛👀🤢.

  • Você não poderá obter versões originais/limpas de sua classe.
  • O TypeScript usa uma versão antiga da proposta de decoradores que não será implementada no padrão ECMAscript 🚨.
  • Ele adiciona código extra em tempo de execução aumentando o processamento ao seu aplicativo.
  • O que é mais importante, no entanto, em termos de verificação de tipo no JSX, é que os decoradores não estendam a definição de tipo da classe. Isso significa (no nosso exemplo) que o nosso componente Container não terá absolutamente nenhuma informação de tipo para o consumidor sobre props adicionadas/removidas.

7. Faça a busca de tipos (state/props) diretamente no componente

📚 Documentação dos Lookup Types

⚠️ Não:

Image for post

✅ Bom:

Image for post

❓Por quê:

  • Exportar props ou state da implementação do seu componente está tornando a superfície da API maior.
  • Você deve sempre fazer uma pergunta, porque os consumidores do seu componente devem ser capazes de importar o tipo explícito state/props? Se eles realmente precisarem disso, eles sempre poderão acessá-lo via "lookup types". Deixando a API mais limpa e as informações de tipo ainda estão lá para todos. Win-Win.
  • Se você precisar fornecer um tipo de props mais complexo, eles devem ser extraídos (ou levantados) vivendo em um arquivo como como models/types e exportado como API Pública.

8. Sempre forneça tipos explícitos para props children

Image for post

✅ Bom:

Image for post

❓Por quê:

  • children prop é anotado como opcional dentro de componentes de classe e de componentes funcional no react.d.ts. Isso apenas espelha a implementação de como o React manipula children. Enquanto isso é ok e tudo mais, eu prefiro ser explícito com a API do componente.
  • Se você planeja usar children para projeção de conteúdo, certifique-se de anotá-lo explicitamente com o tipo de sua escolha e, ao contrário, se o seu componente não o usar, evite o uso do tipo never.

Restrição para o tipo children

Quais tipos podem ser usados ​​para anotar children no TypeScript? Quero dizer, posso restringir para serem apenas um tipo específico de componente? Como é possível com o Flow? Algo como Tab dentro Tabs children: Tab[]?

Infelizmente não 🙃, pois o TypeScript não é capaz de “analisar” a saída de JSX.factory 👉 React.createElement retorna JSX.Element do namespace global, extends React.ReactElement<any> portanto, o que o compilador obtém é um tipo de objeto com a verificação de tipo desativada (ATENÇÃO: toda vez que um any é usado, um gatinho morre).

Ou conforme indicado em documentos do TypeScript:

“Por padrão, o resultado de uma expressão JSX é digitado como any. Você pode personalizar o tipo especificando a interface JSX.Element. No entanto, não é possível recuperar informações de tipo sobre o elemento, atributos ou filhos do JSX dessa interface. É uma caixa preta ⬛️ 📦."

NOTA: O TS 2.8 introduziu namespaces JSX de escopo local, o que pode ajudar a resolver esse recurso no futuro. Fique de olho esse espaço!

Podemos usar os seguintes tipos de anotação children:

  • ReactNode | ReactChild | ReactElement
  • object | {[key:string]:unknown} | MyModel
  • primitivos string | number | boolean
  • Array<T> onde T pode ser qualquer um dos antigos
  • never | null | undefined (null e undefined não fazem muito sentido)

9. Use inferência de tipos para definir state de componentes ou defaultProps

Image for post

✅ Bom:

Image for post

⭐️ Melhor:

Ao congelar initialState/defaultProps, o sistema de tipos readonly inferirá os tipos corretos (quando alguém acidentalmente configurasse algum valor, ele receberia um erro de compilação). Também marcar os dois, static defaultProps e state como readonly dentro da classe, é um toque agradável, para nos impedir de cometer erros em tempo de execução quando definimos incorretamente o estado via this.state = {...}.

Image for post

❓Por quê:

  • Informação de tipos é sempre sincronizado com a implementação sendo a fonte de verdade, é apenas uma coisa, 💙 A implementação! 💙
  • Menos boilerplate de tipos
  • Código mais legível
  • Adicionando o modificador readonly e congelando o objeto, qualquer mutação dentro do seu componente terminará imediatamente com erro de compilação, o que impedirá qualquer erro de execução = consumidores do seu aplicativo felizes!

E se eu quiser usar um tipo mais complicado dentro do state ou defaultProps?

Use o operador as para definir suas propriedades dentro da constante:

Image for post

10. Ao usar uma função como fábrica ao invés de classes para modelos/entidades, aproveite a fusão de declaração, exportando o tipo e a implementação

Image for post

✅ Bom:

Image for post

❓Por quê:

  • Menos boilerplate
  • Um token para o tipo e a implementação/API menor
  • Tanto o tipo quanto a implementação estão em sincronia e, mais importante, a implementação é a fonte da verdade

11. Use a importação padrão para importar React

Image for post

✅ Bom:

Image for post

Para suportar esse comportamento, você precisa definir a seguinte configuração no arquivo tsconfig.json:

{ 
"compilerOptions": {
/ *
Permite a emissão de interoperabilidade entre os módulos CommonJS e ES
através da criação de objetos de namespace para todas as importações.
Implica 'allowSyntheticDefaultImports: true'.
* /
"esModuleInterop": true
}
}

💡 Considerar:

Image for post

NOTA:

  • Com este estilo, a sintaxe de açúcar para usar fragmentos 👉 <></> não funcionará. Você precisa importá-los explicitamente e usar via <Fragment>...</Fragment>.
  • Eu gosto dessa abordagem porque é explícito e posso adicionar key sempre que quiser sem introduzir mudanças "demais" ao fazer a refatoração.

Se você quiser usar o “💡 Considerar” no projeto inteiro sem definir o pragma jsx por arquivo, você precisa definir a seguinte configuração no arquivo tsconfig.json:

{ 
"compilerOptions": {
/ *
Especifique a função de fábrica de JSX para ser usada ao direcionar a emissão de JSX 'react', por exemplo , 'React.createElement' ou 'h'.
* /
"jsxFactory": "createElement"
}
}

❓Por quê:

  • É confuso importar todo o conteúdo da biblioteca React quando você não as está usando.
  • Está mais alinhado com JS idiomático.
  • Você não precisa importar os tipos definidos no namespace React, da maneira que você precisa com Flow, pois o TS suporta fusão de declarações.
  • O exemplo “💡 Considerar” é ainda mais explícito quanto ao que é usado em seu módulo e pode melhorar o tree-shaking durante o tempo de compilação.

12. Evite namespace

Image for post

✅ Bom:

Image for post

❓Por quê:

Se você realmente precisa de algum tipo de namespace no seu módulo, use JavaScript idiomático, como no exemplo a seguir:

Image for post

13. Evite importações de módulos ES2015 ao importar tipos que não tenham código de tempo de execução

Image for post

✅ Bom:

Image for post

NOTA:

Se você está tendo muitas importações duplicadas, considere criar alias para o tipo local 👉 type State = import('./counter').Counter['state']

👉 Cuidado, se você quer criar alias de tipo local a partir da importação de tipo genérico, você precisa espelhar esse tipo genérico, por exemplo: 👉 type ReactElement<T=any> = import('React').ReactElement<T>

❓Por quê:

  • Seu código é explícito tanto para humanos quanto para máquinas. Se você não usar nenhum código de tempo de execução, anote seu código somente via import('<caminho>')
  • Veja este excelente post de David East para saber mais

14. Evite camelCase/PascalCase para nomes de arquivos

SkaterBoy.tsx
userAccessHandlers.ts

✅ Bom:

skater-boy.tsx
user-access-handlers.ts

❓Por quê:

  • Nomes de arquivos legíveis. eg MyHalfFixedDedupedDirResolvervs my-half-fixed-deduped-dir-resolver 👀
  • Não há mais conflitos estranhos no git ao renomear/excluir/adicionar arquivos em diferentes sistemas de arquivos de sistemas operacionais (diferença insensível de maiúsculas e minúsculas)
  • Consistência (eu não tenho que pensar se este arquivo é componente ou algum auxiliar ou serviço. A extensão .tsx me diz isso)
  • Mapeia bem para nome de implementação do componente skater-boy.tsx 👉 const SkaterBoy = () => {}

15. Declare tipos antes da implementação

Image for post

✅ Bom:

Image for post

❓Por quê:

  • As primeiras linhas do documento indicam claramente que tipos de tipos são usados ​​no módulo atual. Esses tipos são apenas para compilação
  • Declarações de tempo de execução e tempo de compilação são claramente separadas
  • O usuário do componente imediatamente sabe a “API” sem precisar fazer alguma rolagem pelo arquivo

NOTA:

Se você precisar fazer algum tipo de fusão como parte de sua API, defina essa fusão após a implementação:

Image for post

16. Evite declarações de métodos dentro de alias de interface/tipo

Image for post

✅ Bom:

Image for post

❓Por quê:

17. Evite number para chave de tipo indexável

Image for post

✅ Bom:

Image for post

❓Por quê:

  • As propriedades do objeto JavaScript são sempre typeof string! Não crie predicados de tipo falso em seus aplicativos!
  • Anotar chaves com number é OK para arrays (definição padrão de matrizes nas libs .d.ts 👉 [n: number]: T;). Embora, na vida real você raramente deveria entrar em situações que você queira definir implementação de matriz "personalizada".

18. Evite JSX.Element para anotar o tipo de retorno de função/componente ou filhos/props

Image for post

✅ Bom:

Image for post

❓Por quê:

  • Globais são ruins
  • O TypeScript suporta o JSX com escopo local para poder suportar vários tipos de fábrica de JSX e a verificação de tipo adequada por fábrica. Enquanto os tipos atuais do React ainda usam namespace JSX global, ele será alterado no futuro.
  • Tipos explícitos ao invés de generalizados

Finalizando

E lembre-se… respeito é tudo! 😁

Como sempre, não hesite em me enviar um ping se você tiver alguma dúvida aqui ou no Twitter (@martin_hotell) e além disso, feliz anotação de tipos galera! E até a próxima! Felicidades! 🖖 🌊 🏄

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store