Artigo original: How to Add TypeScript to a JavaScript Project

Eu amo escrever código e quero ser bom nisso. Porém, por algum motivo, programar em JavaScript nunca foi o meu forte.

Não importa o quanto eu praticava – os mesmos erros continuavam aparecendo em produção: exceções cannot read property <> of undefined, a famosa string [Object object] e, até mesmo, chamadas de funções com um número inválido de parâmetros.


Além do mais, a maioria das bases de código em que eu estava trabalhando tinham uma quantidade de JavaScript realmente grande. Então, aqui está um ótimo diagrama que explica como é estar no meu lugar:

image-1

Neste artigo, evitarei explicar por que o TypeScript é incrível (e ele é) e focarei nas tarefas que você precisa completar se você quiser migrar do JavaScript básico para um projeto que mistura TypeScript.

Até o final do artigo, você será uma pessoa feliz e será capaz de responder às seguintes perguntas:

  • Como eu posso adicionar tipos ao meu projeto em JavaScript?
  • O que é TypeScript?
  • Como eu posso usar TypeScript em um projeto em JavaScript?
  • Quais são as etapas para converter uma aplicação do JavaScript para oferecer suporte a TypeScript?
  • Como eu posso cuidar do build e do empacotamento?
  • Como eu posso tomar conta do lint?
  • Como eu posso "vender" o TypeScript para minha organização e desenvolvedores?

Como eu posso adicionar tipos ao meu projeto em JavaScript?

O JavaScript básico ainda não suporta tipos. Então, precisamos de algum tipo de abstração sobre o JavaScript para fazer isso.

Algumas abstrações comuns são usar o verificador de tipos estático flow, do Facebook, e a linguagem da Microsoft chamada: TypeScript.

Este artigo tratará do uso e da inclusão do TypeScript ao seu projeto em JavasScript.

O que é Typescript?

TypeScript é um superset tipado do JavaScript, que é compilado para JavaScript puro.

superset
Se você sabe JavaScript, já percorreu mais da metade do caminho

O Typescript consiste de algumas partes. A primeira é a linguagem em si — essa é uma nova linguagem que contém todas as features do JavaScript .

A segunda é o compilador do TypeScript, o tsc, que é o motor do sistema de tipos (do inglês, type system engine) que é um mecanismo de compilação que cria arquivos .ts e gera arquivos .js.

"Hello world" em TypeScript

Como exemplo, estes são os passos que você precisa para escrever sua primeira aplicação em TypeScript:

  1. instale o TypeScript com o comando npm i typescript
  2. crie uma pasta chamada example e entre nela com o comando cd example
  3. crie um arquivo chamado hello.world.ts
  4. escreva o código a seguir nele:
const firstWords:string = "hello world"
console.info(firstWords);
Hello world em TypeScript – Meu primeiro programa em TypeScript!

Em seguida, salve-o.

5.  execute o comando npx tsc hello.world.ts  para rodar o compilador do TypeScript no seu arquivo hello.world.ts

6.  perceba que você agora tem um arquivo hello.world.js que você pode executar :)

7.  execute node ./hello.world.js

Como eu posso usar TypeScript em um projeto em JavaScript?

Existem algumas estratégias para fazer essa "migração" (em termos de empresa e de código). Listei essas estratégias abaixo de acordo com seu "custo" e com o valor que elas fornecem.

Eu sugeriria começar com "suporte a TS na aplicação" e seguir em frente depois de provar o valor para sua equipe de desenvolvimento.

typescript-migration-steps
O processo de migração para o TypeScript. Faça todo o processo somente se provar o valor.

A abordagem "um pequeno passo para o homem" – adicionando suporte ao TS em aplicações existentes

small-step
Um pequeno passo para um desenvolvedor. Tipos são maravilhosos :)

Minha primeira sugestão é criar uma mistura das duas linguagens em um único projeto e, então, escrever todos os códigos "futuros" em TypeScript.

A combinação de duas linguagens em um único projeto soa muito ruim no começo, mas funciona muito bem já que o TS foi construído para uso gradual. A princípio, ele pode ser usado apenas como JS com arquivos .ts e linhas de importação estranhas.

Nessa estratégia, compilaremos os arquivos TypeScript migrados e apenas copiaremos os arquivos JavaScript para uma pasta de saída.

O grande benefício dessa abordagem é permitir uma curva de aprendizado gradual para a equipe de desenvolvimento (e para você) com a linguagem e seus recursos. Ela também oferece experiência prática e informações sobre seus prós e contras.

Eu recomendo começar a partir dessa etapa e, em seguida, seguir passo a passo com sua equipe antes de prosseguir. Para saber "como fazer isso" rapidamente, leia abaixo sobre as etapas para converter uma aplicação em JavaScript para que tenha suporte ao TypeScript a parte

A abordagem aberta para negócios – adicionando suporte ao TS para bibliotecas existentes

Depois de ter alguma experiência prática com o TS e de sua equipe de desenvolvimento concordar que vale a pena seguir em frente, sugiro converter suas bibliotecas e módulos internos para oferecer suporte a TS.

Isso pode ser feito de duas maneiras:

A primeira maneira envolve usar arquivos de declaração (texto em inglês). Uma simples adição de arquivos d.ts ajuda o compilador do TS a verificar o tipo de código em JavaScript existente e fornece suporte de preenchimento automático em seu IDE.

Essa é a opção "mais barata", pois não requer nenhuma alteração de código na biblioteca. Ela também oferece potência máxima e suporte a tipos em seu código futuro.

A segunda maneira é realizar uma reescrita completa do TypeScript, o que pode ser demorado e sujeito a erros. Eu desaconselharia isso, a menos que se prove um retorno digno para sua equipe.

O "esqueleto" – um passo para o futuro

future
Um esqueleto de código em TypeScript é o caminho para garantir um futuro brilhante!

Presumo que a maioria dos desenvolvedores seja "preguiçosa" e, geralmente, inicie sua aplicação copiando de um código esqueleto (que geralmente contém registro, métricas, configuração e assim por diante).

Essa etapa ajuda você a navegar em seu caminho para um futuro brilhante, criando um esqueleto "oficial" para sua empresa. Será 100% TS e aposentará o antigo esqueleto em JS, se existir.

Esse typescript-node-starter é um bom primeiro projeto para começar.

A abordagem de "apostar tudo" – convertendo uma base de código completa de JS para TS

allin
Eu aposto tudo! Vamos colocar tipagem em tudo o que aparecer!

Essa opção requer uma reescrita total do código de JavaScript para TypeScript.‌

‌Eu recomendaria fazer isso como uma etapa final no processo de migração do TS, pois requer uma reescrita total da aplicação e profundo conhecimento do TypeScript e de seus recursos.

Você pode reescrever (é um processo longo) da seguinte maneira:

  1. Defina tipos claros para a lógica de negócios, APIs e HTTP de sua aplicação
  2. Use pacotes @types para todas as bibliotecas em seu package.json. A maioria das bibliotecas suporta TS. Nesse processo, sugiro migrar um por um (apenas adicionando @types/<nome_do_pacote> no seu arquivo package.json).
  3. Converta os componentes lógicos de sua aplicação em ordem de importância. Quanto mais exclusiva for a lógica de negócios, melhor.
  4. Converta as partes de E/S de sua aplicação, camadas de banco de dados, filas e assim por diante.
  5. Converta seus testes.
image-3
Usar tipos é motivo para festejar :)

Lembre-se de que existem ferramentas automatizadas projetadas para facilitar esse processo, como, por exemplo, a ts-migrate, do time da Airbnb.

Ela aborda esse problema de uma perspectiva diferente e converte todos os arquivos em TypeScript. Ela também permite melhorias graduais (como mencionado nas etapas acima), enquanto toda a base de código é em TypeScript desde o primeiro dia.

Como converter uma aplicação do JavaScript para oferecer suporte a TypeScript

Instale o TypeScript

executando : npm install typescript.

Arquivo de configuração do Typescript

Adicione um arquivo de configuração do TypeScript, que pode ser criado usando o comando npx tsc --init no seu terminal.

Aqui está um exemplo de como nossa configuração inicial parecia:

{
 "compilerOptions": {
   "target": "esnext",
   "module": "commonjs",
   "allowJs": true,
   "checkJs": false,
   "outDir": "dist",
   "rootDir": ".",
   "strict": false,
   "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
   "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
   "declaration": true, /* Generates corresponding '.d.ts' file. */
   "strictNullChecks": true,
   "resolveJsonModule": true,
   "sourceMap": true,
   "baseUrl": ".",
   "paths": {
    "*": [
      "*",
      "src/*",
      "src/setup/*",
      "src/logic/*",
      "src/models/*",
      "config/*"
    ]
  },
 },
  "exclude": ["node_modules", "dist"],
  "include": [
    "./src",
    "./test",
    "./*",
    "./config" 
  ]
}

Algumas coisas para observar acima:

  • Lemos todos os arquivos na pasta src ou test ou config  (usando a opção include).
  • Aceitamos arquivos em JavaScript como entradas (usando a opção allowJs).
  • Colocamos todos os arquivos de saída na pasta dist (usando a opção outDir).

Crie seu primeiro arquivo .TS no projeto

Eu recomendo começar adicionando um arquivo TypeScript simples (ou alterando um arquivo JS realmente simples para TS) e realizando o deploy. Faça essa migração um passo de cada vez.

Cuide do seu arquivo package.json

Aqui está como era nosso package.json e como ficou depois:

{
  "scripts": {
    "start": "node ./application.js",
    "mocha": "mocha --recursive --reporter spec -r test/bootstrap.js",
    "test": "npm run mocha -- test/ -r test/integration/bootstrap.js", 
  }
}
Antes
{
  "scripts": {
    "start": "node ./dist/application.js",
    "build-dist": "./node_modules/typescript/bin/tsc",
    "mocha": "mocha --recursive --reporter spec -r ./dist/test/bootstrap.js",
    "test": "npm run mocha -- ./dist/test/ -r ./dist/test/integration/bootstrap.js"
  }
}
Depois

Como você pode ver, a maioria das mudanças foi relacionada a adicionar o prefixo dist à maioria dos nossos comandos de compilação. Também adicionamos um script build-dist que compila nossa base de código e move todos os arquivos para uma pasta dedicada chamada dist.

Um dos grandes problemas ao adicionar TypeScript ao seu projeto é que você está adicionando uma camada indireta entre o código que você escreve e o código que realmente é executado na produção (já que o .ts é transpilado para .js em tempo de execução).

Por exemplo, imagine o seguinte programa em TypeScript:

const errorMessage: string = "this is bad"

throw new Error(a)

Quando o executarmos, ele lançará o seguinte erro:

Error: this is bad
    at Object.<anonymous> (/Users/dorsev/work/git/example/hello.js:3:7)
Ué? Temos apenas 2 linhas em nosso código em TypeScript!

Isso é problemático, pois nossa base de código contém apenas arquivos .ts. Como a maioria dos códigos de produção contém centenas de linhas, será muito demorado traduzir esses números e arquivos corretamente.

Felizmente, para nós, existe uma solução para isso chamada source-map-support!

Isso nos permite garantir que os erros tenham nomes de arquivo .ts e números de linha adequados, como estamos acostumados. :)

Isso pode ser feito executando npm install source-map-support e adicionando a seguinte linha nas primeiras linhas da sua aplicação:

require('source-map-support').install();

O código agora se parece com isso:

require('source-map-support').install();
const a:string = "this is bad"
throw new Error(a)
hello.world.ts

Quando o compilamos, executamos tsc --sourcemap hello.ts. Agora, obtemos o seguinte erro, que é incrível! :)

Error: this is bad
    at Object.<anonymous> (/Users/dorsev/work/git/example/hello.ts:3:7)

Nas versões recentes do nodejs, isso é suportado nativamente usando a flag --enable-source-maps.

Como cuidar do seu build (Travis) e do empacotamento

Vamos apenas examinar as alterações antes e depois em nosso arquivo de build.

Nosso arquivo .travis, antes, estava assim (versão simplificada):

jobs:
  include:
  - &build-and-publish
    before_script:
    - npm install --no-optional --production
    - npm prune --production
    before_deploy:
     - XZ_OPT=-0 tar --exclude=.git --exclude=reports.xml --exclude=${ARTIFACTS_MAIN_DIR}
       --exclude=.travis.yml --exclude=test -cJf "${ARTIFACTS_PATH}/${REPO_NAME}".tar.xz * .??*
  
  - &test
    before_script:
     - npm install --no-optional
    script:
     - echo "Running tests"
     - npm run lint && npm test
.travis antes do TypeScript

Foi assim que ele ficou depois:

jobs:
  include:
  - &build-and-publish
    before_script:
    - npm install --no-optional --production
    - npm run build-dist  # Build dist folder
    - npm prune --production
    before_deploy:
     - cp -rf config/env-templates ./dist/config/
     - cp -rf node_modules ./dist/
     - cd dist
     - XZ_OPT=-0 tar --exclude=.git --exclude=reports.xml --exclude=${ARTIFACTS_MAIN_DIR} --exclude=.travis.yml --exclude=test -cJf "${REPO_NAME}.tar.xz" *
     - mv ${REPO_NAME}.tar.xz "../${ARTIFACTS_PATH}"
     - cd ..

  - &test
    before_script:
     - npm install --no-optional
     - npm run build-dist
    script:
     - echo "Running tests"
     - npm run lint && npm test
.travis depois do TypeScript

Observe que a maioria das alterações diz respeito ao "empacotamento" do arquivo  tar.xz e à execução do comando build-dist antes de acessar a pasta dist.

Como posso cuidar do lint?

Existem algumas soluções de lint disponíveis.

A primeira solução que usamos foi tsfmt  –  mas decidimos contra isso mais tarde porque exige que você mantenha duas configurações separadas para seu projeto (uma para TypeScript usando tsfmt e outra separada para JavaScript usando eslint). O projeto também parece descontinuado.

Então, encontramos o TSLint , que nos redirecionou para o plug-in slint para TypeScript. Configuramos o TSLint da seguinte maneira:

Essa era o nosso eslintrc.js:

module.exports = {
    rules: {
        indent: [2, 2, {
            SwitchCase: 1
        }],
        'no-multi-spaces': 2,
        'no-trailing-spaces': 2,
        'space-before-blocks': 2,
    },
    overrides: [{
        files: ['**/*.ts'],
        parser: '@typescript-eslint/parser',
        plugins: ['@typescript-eslint'],
        extends: ['plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended']
    }]
}

Configuramos o lint para rodar usando um comando lint-fix em nosso package.json, que se parece com o seguinte:

{
    "scripts": {
        "lint-fix": "node_modules/.bin/eslint . --fix"
    },
    "pre-commit": ["lint-fix"]
}

Como "vender" o TypeScript para seu time de desenvolvedores

Acredito que um dos aspectos mais críticos da introdução do TypeScript em sua organização é o "argumento" e como você o apresenta à sua equipe de desenvolvimento.

Aqui está a apresentação que fizemos internamente, que girava em torno dos seguintes temas:

  1. Explicar por que achamos o TypeScript incrível
  2. O que é TypeScript
  3. Alguns exemplos básicos de código. O ponto principal nesta parte não é "ensinar" 100% sobre o TypeScript, já que as pessoas farão isso por conta própria. Em vez disso, devemos dar às pessoas a sensação de que elas podem ler e escrever TypeScript e que a curva de aprendizado não é tão difícil.
  4. Exemplos de código avançado, como Tipos de União (em inglês, union types) e tipos de dados algébricos (em inglês, algebraic data types) que agregam um grande valor a um desenvolvedor de JS. Este é um verdadeiro deleite, além da linguagem tipada e do compilador, que atrairão seus desenvolvedores.
  5. Como começar a usá-lo: incentive as pessoas a baixar o IDE do vscode e a adicionar uma anotação (//@ts-check) para que possam começar a ver a mágica! Na nossa empresa, preparamos com antecedência alguns erros muito legais que o ts-check detecta, e realizamos uma demonstração ao vivo (de 2 a 3 minutos) para mostrar a rapidez do compilador do TypeScript e como ele pode ajudar usando a documentação do JS com anotações de tipo ou o ts-check.
  6. Aprofunde-se em alguns recursos: explique que os arquivos ts.d e pacotes @types são algumas das coisas que você encontrará bem cedo em suas bases de código em TypeScript.
  7. Pull requests ao vivo do seu trabalho. Mostramos o PR que criamos desde o início e incentivamos as pessoas a revisá-lo e experimentá-lo por si mesmos.
  8. Compartilhe alguns recursos interessantes. Há muito conteúdo on-line e é difícil distinguir o bom do ruim. Faça uma pesquisa sólida para seus colegas de equipe e tente encontrar conteúdo de qualidade sobre as ferramentas que você usa e precisa. Role até a conclusão para conferir meus recursos.
  9. Crie um pull request público . Eu recomendo tentar obter o máximo de apoio possível para sua aprovação.
Screen-Shot-5780-10-20-at-10.09.59-AM
Adicionando TypeScript a um projeto! viva!

10.  Crie um burburinho positivo em sua organização sobre a mudança!

Screen-Shot-5780-10-20-at-10.13.33-AM

Eu recomendo ajustar essa lista de acordo com sua equipe, padrões e restrições de tempo.

Conclusão

O Typescript é mesmo incrível! Se você está escrevendo software de nível de produção e os requisitos de negócios e a disponibilidade são altos, eu o encorajo fortemente a experimentar o TypeScript.

Apenas lembre-se de dar um passo de cada vez. Novas linguagens e frameworks são difíceis. Assim, reserve um tempo para aprender e educar a si mesmo e sua equipe antes de levar esse processo adiante.

Crie um ciclo curto de feedback e propostas de melhorias. É difícil "vender" uma nova linguagem para sua equipe e gerenciamento, pois leva tempo e recursos.

Portanto, crie seu processo de migração com ciclos de feedback curtos, tente definir indicadores de desempenho claros (menos bugs na produção, tempos de refatoração mais fáceis e assim por diante) e certifique-se de que a proposta de valor para o seu caso de uso seja constantemente justificada até que se torne o padrão de fato.

Torne os recursos de aprendizado facilmente acessíveis. Gostei muito desta apresentação sobre os primeiros passos do TypeScript (em inglês) e deste artigo (também em inglês) sobre a migração incremental para o TypeScript.

Além disso, não perca os projetos deno e ts-node. Particularmente, estou muito animado e ansioso para utilizar os dois em breve.