TypeScript: Os 5 mandamentos ao usar TypeScript
Tags: ptbr, typescript, webdev, javascript
Published: 2019-09-23 23:36:44

[_Créditos da Imagem_](https://unsplash.com/photos/1O77vgBVkXQ)

Mais e mais projetos e equipes estão adotando o TypeScript. No entanto, há uma enorme diferença entre usar o TypeScript e tirar o máximo proveito dele.

Apresento a você esta lista de práticas de alto nível recomendadas para TypeScript que ajudarão você a aproveitar ao máximo suas funcionalidades.

Este artigo também está disponível em russo: [5 заповедей TypeScript-разработчика](https://habr.com/ru/post/461565) (por [Vadim Belorussov](https://dev.to/bevalorous)).

# Não minta

Tipos são um contrato. O que isso significa? Quando você implementa uma função, seu tipo é uma promessa para outros desenvolvedores (ou para você no futuro)!

No exemplo a seguir, o tipo da promessa `getUser` retornará um objeto que sempre terá duas propriedades: `name` e `age`.

```tsx
interface User {
  name: string;
  age: number;
}

function getUser(id: number): User { /* ... */ }
```

TypeScript é uma linguagem muito flexível. Está cheio de suposições feitas para facilitar sua adoção. Por exemplo, TypeScript permite implementar `getUser` assim:

```tsx
function getUser(id: number): User {
  return { age: 12 } as User;
}
```

Não faça isso! Isso é mentira. Ao fazer isso, você mente para outros desenvolvedores também (que usarão essa função em suas funções). Eles esperam que o objeto retornado em `getUser` sempre tenha algum `name`. Mas isso não acontece! Então, o que acontece quando seu companheiro de equipe escreve `getUser(1).name.toString()`? Você conhece bem...

Claro, essa mentira parece muito óbvia. No entanto, ao trabalhar com uma enorme base de código, você geralmente se encontra em uma situação em que um valor que deseja retornar (ou passar) quase corresponde ao tipo esperado. Descobrir o motivo da incompatibilidade de tipos leva tempo e esforço e você está com pressa ... então decide fazer utilizar _type casting_ com `as`.

No entanto, ao fazer isso, você viola o contrato, que é sagrado! É sempre melhor dedicar um tempo para descobrir por que os tipos não combinam do que para fazer _type casting_. É muito provável que algum bug em tempo de execução esteja oculto sob a superfície.

Não minta. Respeite seus contratos.

# Seja preciso

Tipos são documentação. Quando você documenta uma função, não deseja transmitir o máximo de informação possível?

```tsx
// Retorna um objeto
function getUser(id) { /* ... */ }

// Retorna um objeto com duas propriedades: name e age
function getUser(id) { /* ... */ }

// Se id for um número e um usuário com esse id existe,
// retorna um objeto com duas propriedades: name e age
// Caso contrário, retorna undefined
function getUser(id) { /* ... */ }
```

Qual comentário em `getUser` você prefere? Quanto mais você souber sobre o que a função retorna, melhor. Por exemplo, sabendo que poderia retornar undefined, você pode escrever uma instrução `if` para verificar se o valor retornado está definido antes de acessar suas propriedades.

É exatamente o mesmo com os tipos. Quanto mais preciso for um tipo, mais informações ele transmite.

```tsx
function getUserType(id: number): string { /* ... */ }

function getUserType(id: number): 'standard' | 'premium' | 'admin' { /* ... */ }
```

A segunda versão do `getUserType` é muito mais informativa e, portanto, coloca o chamador em uma situação muito melhor. É mais fácil manipular um valor se você souber o que ele é com toda a certeza (contratos, lembra?), uma das três _strings_, ao invés de saber que ele pode ser _qualquer string_. Para iniciantes, você tem certeza de que o valor não é uma sequência vazia.

Vamos ver um exemplo mais realista. O tipo `State` representa o estado de um componente que busca alguns dados do back-end. Esse tipo é preciso?

```tsx
interface State {
  isLoading: boolean;
  data?: string[];
  errorMessage?: string;
}
```

O consumidor desse tipo deve lidar com algumas combinações improváveis ​​de valores de propriedade. Por exemplo, não é possível que ambos `data` e `errorMessage` sejam definidos (a busca de dados pode ser bem-sucedida ou resultar em um erro).

Podemos tornar um tipo muito mais preciso com a ajuda de tipos de união discriminados:

```tsx
type State =
   | { status: 'loading' }
   | { status: 'successful', data: string[] }
   | { status: 'failed', errorMessage: string };
```

Agora, o consumidor desse tipo tem muito mais informações. Eles não precisam lidar com combinações ilegais de valores de propriedades.

Seja preciso. Transmita o máximo de informações possível em seus tipos!

# Comece com tipos

Como os tipos são contrato e documentação, eles são ótimos para projetar suas funções (ou métodos).

Existem muitos artigos na Internet que aconselham os engenheiros de software a pensar antes de escreverem o código . Eu concordo totalmente com essa abordagem. É tentador pular direto para o código, mas geralmente leva a algumas decisões ruins. Passar algum tempo pensando na implementação sempre compensa.

Os tipos são super úteis nesse processo. Pensar pode resultar na anotação das assinaturas de tipo das funções envolvidas na sua solução. É incrível porque permite que você se concentre **no que** suas funções fazem, ao invés **de como** elas fazem.

O React.js tem um conceito de componentes de ordem superior. São funções que aumentam determinado componente de alguma maneira. Por exemplo, você pode criar um componente `withLoadingIndicator` que adiciona um indicador de carregamento a um componente existente.

Vamos escrever a assinatura de tipo para esta função. Ele pega um componente e retorna um componente. Podemos usar o React `ComponentType` para indicar um componente.

`ComponentType` é um tipo genérico parametrizado pelo tipo de propriedades do componente. `withLoadingIndicator` pega um componente e retorna um novo componente que mostra o componente original ou mostra um indicador de carregamento. A decisão é tomada com base no valor de uma nova propriedade booleana `isLoading`. Portanto, o componente resultante deve exigir as mesmas propriedades que o componente original mais a nova propriedade.

Vamos finalizar o tipo. `withLoadingIndicator` pega um componente de um tipo em `ComponentType<P>` que `P` denota o tipo das propriedades. Retorna um componente com propriedades aumentadas do tipo `P & { isLoading: boolean }`.

```tsx
const withLoadingIndicator = <P>(Component: ComponentType<P>) 
    : ComponentType<P & { isLoading: boolean }> =>
        ({ isLoading, ...props }) => { /* ... */ }
```

Descobrir o tipo dessa função nos forçou a pensar sobre sua entrada e saída. Em outras palavras, nos fez projetá-la. Escrever a implementação é um pedaço de bolo agora.

Comece com tipos. Deixe os tipos forçarem você a projetar antes de implementar.

# Abrace o rigor

Os três primeiros pontos exigem que você preste muita atenção nos tipos. Felizmente, você não está sozinho na tarefa - o compilador TypeScript geralmente informa quando seus tipos estão ou quando não são precisos o suficiente.

Você pode tornar o compilador ainda mais útil ativando o sinalizador `--strict` do compilador. É uma bandeira meta que permite que todas as opções estritas verificação de tipo: `--noImplicitAny`, `--noImplicitThis`, `--alwaysStrict`, `--strictBindCallApply`, `--strictNullChecks`, `--strictFunctionTypes` e `--strictPropertyInitialization`.

O que eles fazem? Em geral, habilitá-los resulta em mais erros do compilador TypeScript. Isso é bom! Mais erros do compilador significam mais ajuda do compilador.

Vamos ver como a ativação `--strictNullChecks` ajuda a identificar algumas mentiras.

```tsx
function getUser(id: number): User {
    if (id >= 0) {
        return { name: 'John', age: 12 };
    } else {
        return undefined;
    }
}
```

O tipo de `getUser` diz que sempre retornará um `User`. No entanto, como você pode ver na implementação, ele também pode retornar um valor `undefined`!

Felizmente, ativando `--strictNullChecks` retorna um erro do compilador:

```tsx
Type 'undefined' is not assignable to type 'User'.
```

O compilador TypeScript detectou a mentira. Você pode se livrar do erro dizendo a verdade:

```tsx
function getUser(id: number): User | undefined { /* ... */ }
```

Abrace o tipo de verificação da rigidez. Deixe o compilador observar seus passos.

# Mantenha-se atualizado

A linguagem TypeScript está sendo desenvolvida em um ritmo muito rápido. Há um novo lançamento a cada dois meses. Cada versão traz melhorias significativas no idioma e novos recursos.

Geralmente, os novos recursos de idioma permitem tipos mais precisos e uma verificação mais rigorosa.

Por exemplo, a versão 2.0 introduziu Tipos de União Discriminada (que eu mencionei no tópico **Seja preciso**).

Versão 3.2 introduziu a opção `--strictBindCallApply` do compilador que permite a digitação correta das funções `bind`, `call` e `apply`.

[A versão 3.4 melhorou a inferência de tipo em funções de ordem superior](https://codewithstyle.info/TypeScript-3-4-hidden-gem-propagated-generic-type-arguments/), facilitando o uso de tipos precisos ao escrever código em estilo funcional.

O que quero dizer aqui é que realmente vale a pena conhecer os recursos de linguagem introduzidos nas versões mais recentes do TypeScript. Eles geralmente podem ajudá-lo a aderir aos outros quatro mandamentos desta lista.

Um bom ponto de partida é o [roteiro oficial do TypeScript](https://github.com/Microsoft/TypeScript/wiki/Roadmap). Também é uma boa idéia verificar a [seção TypeScript do Microsoft Devblog](https://devblogs.microsoft.com/typescript/) regularmente, pois todos os anúncios de lançamento são feitos por lá.

Mantenha-se atualizado com os novos recursos de idioma e deixe a linguagem fazer o trabalho para você.

# Finalizando

Espero que você ache essa lista útil. Como qualquer coisa na vida, esses mandamentos não devem ser seguidos cegamente. No entanto, acredito firmemente que essas regras o tornarão um programador TypeScript melhor.

Eu adoraria ouvir sua opinião sobre isso na seção de comentários.

---

# Créditos

- [5 Commandments for TypeScript programmers](https://codewithstyle.info/5-commandments-for-TypeScript-programmers/), escrito originalmente por [Milosz Piechocki](https://twitter.com/miloszpp)