Na prática: TypeScript + ESLint + Prettier + EditorConfig + Jest, Supertest...

Este material tem como finalidade apresentar de forma prática como configurar a suíte de ferramentas mencionadas no titulo deste material. Ou seja, este material não visa ser didático, mas sim prático. Paralelamente a isso, estou criando uma série de documentos com abordagem didática sobre o mesmo tema…
📚 Livros
No meu repositório no GitHub, você encontrará um link para o download de alguns livros publicados por mim. Além disso, poderá acompanhar o desenvolvimento das minhas obras mais recentes, como o livro sobre Node.js para iniciantes. Acesse: https://github.com/fabiojaniolima/livros
🔗 Sugestão de leitura
Os posts a seguir são meras sugestões ou recomendações de leitura e não devem ser considerados pré-requisitos para a leitura ou execução das práticas descritas neste material:
- Introdução ao Node.js (Single-Thread, Event-Loop e mercado)
- NVM - Gerencie múltiplas instalações do Node.js
- Node.js + Express + ES6 + ESLint + Prettier + Sucrase de forma simples e rápida
- Node.js: web scraping com Puppeteer
- EditorConfig - Padronizando características essenciais
Dica: Recomendo fortemente o uso do ESLint, Prettier e EditorConfig em todos os projetos, pois é uma boa prática manter a consistência em diversos aspectos.
❗️Pré-requisitos
- Node.js
- NPM, Yarn…
- VSCode*
Ao longo deste material, utilizarei o NPM como gerenciador de pacotes. No entanto, você está livre para usar o Yarn ou qualquer outro gerenciador de sua preferência. Quanto ao editor de código, farei uso do VSCode, mas sinta-se à vontade para escolher o editor que mais lhe agrada.
📦 Pacotes utilizados
- typescript
- tsx
- supertest
- rimraf
- eslint
- prettier
- jest
- dotenv
- cors
- express
Observe que optei pelo ESLint em substituição ao TSLint. Esta escolha deve-se ao fato de que, atualmente, o ESLint oferece suporte completo às funcionalidades do TypeScript. Adicionalmente, a equipe de desenvolvimento do TSLint tem incentivado essa migração e concentrado esforços no desenvolvimento do ESLint.
🧑💻 Mãos a obra
Para os passos a seguir, estou partindo do pressuposto de que você já inicializou um diretório como um projeto Node. Por exemplo:
npm init -y
EditorConfig
Este é um daqueles plugins de uso geral, ou seja, independe da tecnologia. Confira minha configuração de uso geral:
root = true
[*]
charset = utf-8
indent_size = 2
end_of_line = lf
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
indent_size = 4
Para saber mais, leia meu artigo: EditorConfig - Padronizando características essenciais
Configurações iniciais
Vamos começar instalando nossas primeiras dependências de desenvolvimento:
npm install typescript @types/node rimraf tsx -D
typescript: instala o TypeScript e utilitário tsc.@types/node: oferece definições de tipos para o conjunto nativo de APIs/módulos do NodeJS.rimraf: será usado como um utilitário para excluir automaticamente builds antigas.tsx: permite executar TypeScript em ambiente de desenvolvimento.
Para gerar o tsconfig.json (arquivo de definições do TypeScript), execute o comando a seguir:
npx tsc --init
O arquivo gerado é autoexplicativo, contendo configurações e suas respectivas explicações em comentários. Edite o arquivo tsconfig.json, mantendo apenas as configurações a seguir:
{
"compilerOptions": {
/* Language and Environment */
"target": "es2021",
"lib": ["ES2021"],
/* Modules */
"module": "commonjs",
"rootDir": "./src",
/* JavaScript Support */
"allowJs": true,
"checkJs": true,
/* Emit */
"outDir": "./build",
"sourceMap": false,
"removeComments": true,
/* Interop Constraints */
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
/* Type Checking */
"strict": true,
"skipLibCheck": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "build"]
}
Edite o arquivo package.json e adicione o alias de build ao bloco scripts:
{
"name": "project",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"build": "rimraf ./build && tsc",
//...omitido
⚠️ Após a configuração do arquivo
tsconfig.json, é comum se deparar com um erro do seguinte tipo:No inputs were found in config file '/home/fabio/project/tsconfig.json'. Specified 'include' paths were '["src/**/*"]' and 'exclude' paths were '["node_modules","build"]'.
Caso isso ocorra, não há motivo para preocupação. Esse erro acontece porque, até o momento, não existe código no projeto, e o transpiler está sinalizando justamente essa ausência.
ESLint e Prettier
Para assegurar uma boa sintaxe e formatação, é essencial utilizar estes dois recursos em conjunto. Para instalá-los, execute o seguinte comando:
npm install eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-import eslint-plugin-simple-import-sort -D
eslint: responsável pelo lint do código, verifica erros de sintaxe, formatação e outros aspectos.prettier: focado na formatação e padronização do código.eslint-config-prettier: desativa as regras do ESLint que podem entrar em conflito com o Prettier.eslint-plugin-prettier: permite executar o Prettier como uma regra do ESLint.eslint-plugin-import: aplica regras de agrupamento e validação para imports e exports.eslint-plugin-simple-import-sort: organiza os imports em ordem alfabética.
Execute o comando abaixo para gerar o arquivo de configuração .eslintrc.*:
npm init @eslint/config
Reproduza as respostas conforme abaixo:
- O ESLint será configurado com foco na verificação de sintaxe e detecção de problemas:

- Utilizaremos ECMAScript Modules (import/export):

- Não utilizaremos nenhum framework:

- Utilizaremos TypeScript:

- Nossa app é uma aplicação backend, ou seja, roda do lado do servidor com NodeJS:

- O arquivo de configuração do ESLint será gerado em formato JSON:

- Pode ser necessário instalar alguns pacotes adicionais, sendo assim selecione Yes:

- Selecione seu gerenciador de pacotes:

Edite o arquivo .eslintrc.json e ajuste os blocos extends, plugins, ignorePatterns e rules conforme abaixo:
{
"env": {
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:prettier/recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["@typescript-eslint", "import", "simple-import-sort"],
"ignorePatterns": ["build", "coverage", "package-lock.json"],
"rules": {
"import/first": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error"
},
"overrides": [
{
"files": ["*.js"],
"rules": {
"@typescript-eslint/no-var-requires": "off"
}
}
]
}
Uma vez que o arquivo de configurações do ESLint está pronto, é hora de configurar as regras que desejamos aplicar no Prettier. Para configurar o Prettier, crie na raiz do projeto um arquivo chamado .prettierrc.json com o seguinte conteúdo:
{
"semi": true,
"tabWidth": 2,
"printWidth": 80,
"singleQuote": true,
"arrowParens": "always",
"trailingComma": "all"
}
As configurações mencionadas acima refletem apenas uma preferência pessoal para formatação estética. Portanto, sinta-se à vontade para desenvolver seu próprio estilo, caso prefira. Abaixo, explico o significado de cada uma das opções utilizadas:
semi: quandotrue, indica que a linha deve terminar com ponto e vírgulatabWidth: define o número de espaços utilizados para a indentação.printWidth: estabelece o comprimento máximo da linha de código antes de aplicar uma quebra de linha.singleQuote: quandotrue, especifica o uso de aspas simples como padrão.arrowParens: sempre utiliza parênteses ao redor de um único parâmetro em funções arrow.trailingComma: adiciona vírgulas ao final de listas multilinhas, facilitando a adição de novos itens sem esquecer da vírgula anterior.
Observação: a lista completa de regras/opções disponíveis pode ser encontrada no link: https://prettier.io/docs/en/options.html.
Edite o package.json para incluir um novo alias/script chamado lint:fix conforme abaixo:
{
"name": "project",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"build": "rimraf ./build && tsc",
"lint:fix": "eslint --fix --ext .js,.mjs,.ts,.json .",
//...omitido
A instrução lint:fix possibilita a execução do ESLint via terminal, isto é, em modo CLI (Interface de Linha de Comando). Para habilitar a funcionalidade de correção automática ao salvar um arquivo (autoFixOnSave), é necessário configurar essa opção no seu editor de código.
Jest
Instalando dependências:
npm install jest ts-jest eslint-plugin-jest @types/jest -D
Para criar o arquivo de definições do Jest (jest.config.js), execute:
npx jest --init
Um questionário será exibido. Responda a este questionário de acordo com as referências fornecidas abaixo:

O passo 4 possibilita trabalhar com a cobertura de código. Isso significa que podemos especificar caminhos a serem monitorados pelo Jest, que, por sua vez, irá reportar a porcentagem do código coberta por testes e identificar as partes não cobertas. Caso prefira não utilizar esse recurso, basta responder 'n'.
Prossiga editando o arquivo jest.config.js. Localize as variáveis preset e testMatch e defina-as com os valores a seguir:"
preset: 'ts-jest',
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
Na configuração acima estamos buscando testes que estejam dentro de um diretório
__tests__, não importa onde esse esteja dentro do projeto, arquivos*.spec.tsou*.test.ts.
Agora, edite o arquivo .eslintrc.json e adicione a propriedade "jest": true conforme abaixo:
{
"env": {
"es2021": true,
"node": true,
"jest": true
//...omitido
Caso deseje utilizar a funcionalidade de Cobertura de Código do Jest, será necessário editar o arquivo jest.config.js. Localize e defina as variáveis da seguinte maneira:
collectCoverageFrom: ['<rootDir>/src/services/**/*.ts'],
coverageReporters: ["text-summary", "lcov"],
No campo collectCoverageFrom, informe um array com os caminhos dos arquivos que devem ser incluídos na cobertura. Por exemplo, para cobrir outro diretório no mesmo nível de services, adicione uma expressão regular: <rootDir>/src/{services,helpers}/**/*.ts. Para mais padrões de expressões regulares, consulte a documentação oficial.
Os resultados da Cobertura de Código serão salvos no diretório especificado pela variável coverageDirectory. Não se esqueça de adicionar este diretório ao .gitignore.
Agora, crie o arquivo __tests__/sum.ts e insira o seguinte conteúdo:
describe('Only one test', () => {
it('Sum 1 + 1 = 2', () => {
expect(1 + 1).toBe(2)
})
})
O arquivo mencionado é utilizado exclusivamente para verificar se o Jest está operando corretamente. Agora, proceda com a execução do seguinte comando:
npm run test
Como resultado, teremos o seguinte output na console:

Vamos avaliar cada uma das partes retornadas:
- Resultado de cada uma das etapas.
- Resumo da Cobertura de Testes (Coverage Summary). Lembre-se de que, no arquivo
jest.config.js, configuramos a opçãocollectCoverageFrom. O Coverage Summary calcula a cobertura de testes para os caminhos especificados nessa configuração. - Resumo do teste
Realmente é impressionante! No entanto, a cereja do bolo se encontra em ./coverage/lcov-report/index.html. Abra esse arquivo no seu navegador para visualizar a cobertura dos seus testes.
Como ainda não temos código sendo testado em nossa aplicação, o resultado será uma página sem dados de cobertura.
Agora, será necessário editar o tsconfig.json e adicionar coverage, __tests__ e **/*.spec.ts à lista de itens a serem ignorados:"
"exclude": [
"node_modules",
"build",
"coverage",
"**/__tests__",
"**/*.spec.ts",
"**/*.test.ts"
]
Scripts e rotinas de execução (package.json)
Edite o package.json, localize a sessão main e scripts e altere estas conforme abaixo:
"main": "./build/start/server.js",
"scripts": {
"build": "rimraf ./build && tsc",
"start:dev": "tsx ./src/start/server.ts",
"start:inspect": "tsx --inspect-brk ./src/start/server.ts",
"start:watch": "tsx watch ./src/start/server.ts",
"lint:fix": "eslint --fix --ext .js,.mjs,.ts,.json .",
"test": "jest"
}
main: define o ponto de entrada da aplicação. Aqui, ele aponta para o diretório de build, ou seja, ao executarnode ., este arquivo será o inicializado.build: emprega orimrafpara remover o diretório./builde, em seguida, compila os arquivos JavaScript usando o tsc.start:dev: inicializa o servidor em modo desenvolvimento.start:inspect: inicializa o servidor em modo desenvolvimento com inspect ativo para debug — no final deste material mostrarei como fazer o debug pelo VSCode.start:watch: inicia o servidor de desenvolvimento em modo de observação ('watch').lint:fix: habilita a execução do ESLint através do terminal.test: executa o Jest.
Para rodar qualquer um dos scripts definidos, utilize o comando:
npm run nome_do_script
Por exemplo:
npm run lint:fix
⚠️ Como nosso diretório
./srcainda não contém arquivos TypeScript, a execução de qualquer outro script resultará em erro. Não se preocupe, pois isso é esperado."
Express
Vamos as dependências:
npm install express dotenv cors
express: micro-framework simples e flexível;cors(cross-origin): é uma especificação que define meios para que um recurso do servidor seja acessado remotamente via web/rede; Resumidamente, o cors permite que nossa aplicação seja acessada de um endereço externo;dotenv: é um módulo de dependência zero, responsável por carregar variáveis de ambiente de um arquivo.envemprocess.env.*.
Será necessário instalar as definições de tipo do Express:
npm install @types/express @types/cors -D
Agora crie na raiz do projeto um arquivo .env e atribua o valor abaixo a ele:
APP_PORT=3000
Este arquivo irá centralizar informações sensíveis da aplicação e por isso não é versionado. Crie um arquivo
.env-samplede amostra, este pode conter informações pre-preenchidas não sensíveis. Exemplo:DB_HOST="localhost"
DB_PORT=5432
DB_NAME=
DB_USER=
DB_PASS=
Crie o arquivo de rotas ./src/routes.ts:
import { Router } from 'express';
const router = Router();
router.get('/', (_, res) => {
res.json({ message: 'Hello world!' });
});
export default router;
Agora crie o arquivo ./src/start/app.ts com o seguinte conteúdo:
import 'dotenv/config'
import cors from 'cors'
import express, { Errback, Request, Response } from 'express'
import router from '../routes'
const app = express()
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use(router)
app.use((_: Request, res: Response) => {
res.status(404).json({ message: 'Page Not Found' })
})
app.use((_: Errback, _req: Request, res: Response) => {
return res.status(500).json({ message: 'Internal Error' })
})
export default app
Crie o arquivo ./src/start/server.ts, esse arquivo é o responsável por subir o servidor na porta informado no arquivo .env:
import app from './app'
const port = parseInt(process.env.APP_PORT as string) || 3000
app.listen(port, () => {
console.log('\x1b[33m%s\x1b[0m', `=> 🚀 Server running on the port: ${port}`)
})
Para testar o resultado basta executar na raiz do projeto:
npm run start:dev
Se tudo correu bem teremos como retorno o seguinte output:

Show de bola! Nossa aplicação está rodando na porta 3000, para acessar basta chamar: http://localhost:3000 e teremos um lindo e belo JSON como output:
{ "message": "Hello World!" }
Supertest
Bom, nossa aplicação com Express é extremamente simples, porém, suficiente para realizarmos testes reais com o Jest. Nossa missão é disparar uma requisição contra a rota raiz (GET /) e verificar se o status de retorno é 200 e se a propriedade message foi devolvida.
Para facilitar nossa vida vamos instalar uma lib projetada para lidar com testes que envolvem requisições HTTP:
npm install supertest @types/supertest -D
Você pode excluir o arquivo de teste
__tests__/sum.tsse preferir, afinal de contas ele tinha um único objetivo, verificar se o Jest estava de fato funcionando.
Edite novamente o arquivo jest.config.js e:
...// Por conta de limitação aqui da plataforma (body < 20000 caracteres) eu não consigo postar o conteúdo completo. Continue lendo em: https://fabiojanio.medium.com/na-prática-typescript-eslint-prettier-editorconfig-jest-supertest-7206684ab55d