Montar proyecto NodeJS con TypeScript
Tags: spanish, node, jest, typescript
Published: 2021-08-13 22:13:53

# Introducción

En este tutorial vamos a crear un proyecto de cero que esté preparado para trabajar con TypeScript. 

#Vamos al lío

## Configuración inicial con TypeScript
Lo primero será crear una carpeta para nuestro proyecto y situarnos dentro:
```sh
$> mkdir ts-playground
$> cd ts-playground
```
Ahora dentro de la carpeta inicializamos el proyecto con Node:
```bash
$> npm init -y
```
E instalamos la dependencia de TypeScript para desarrollo:
```bash
$> npm install --save-dev typescript
```
Creamos el fichero tsconfig.json con los valores por defecto:
```bash
$> npx tsc --init
```
Y editamos el fichero tsconfig.json recién creado para dejarlo con las siguientes propiedades:
```json
{
 "compilerOptions": {
  "target": "es5",
   "module": "commonjs",
   "outDir": "./dist",
   "rootDir": "./src",
   "strict": true,
   "noImplicitAny": true,
   "strictNullChecks": true,
   "esModuleInterop": true,
   "skipLibCheck": true,
   "forceConsistentCasingInFileNames": true
},
 "exclude": [
   "node_modules",
   "**/*.spec.ts"
  ]
}
```
Creamos la carpeta src donde vamos a tener nuestros ficheros *.ts
```bash
$> mkdir src
```

Editamos el fichero package.json para incluir los siguientes scripts:
```json
"scripts": {
   "build": "tsc --build tsconfig.json",
   "build:watch": "tsc --build tsconfig.json --watch"
},
```
Probamos que la build se genera correctamente creando el fichero src/calc.ts con el siguiente contenido (src/calc.ts):
```typescript
export class Calc {
 add (a: number, b: number): number {
   return a + b
 }
}
```
Ahora ejecutamos:
```bash
$> npm run build
```
Y tenemos que ver que el fichero dist/calc.js se genera correctamente.

## Configuración de Jest

Para la integración con Jest vamos a instalar las siguientes dependencias de desarrollo:
```bash
$> npm install --save-dev jest ts-jest @types/jest
```
- jest - Es el test runner
- ts-jest - Es un preprocesador de Jest para TypeScript
- @types/jest - Es la librería de definición de tipos de Jest

Para la configuración de Jest vamos a crear en la raíz del proyecto el fichero jest.config.js con el siguiente contenido:
```javascript
module.exports = {
 roots: [
   "<rootDir>/src"
 ],
 testMatch: [
   "**/__tests__/**/*.+(ts|tsx|js)",
   "**/?(*.)+(spec|test).+(ts|tsx|js)"
 ],
 transform: {
   "^.+\\.(ts|tsx)$": "ts-jest"
 },
 collectCoverageFrom: [
   "**/*.{js,jsx,ts,tsx}",
   "!**/*.d.ts",
   "!**/node_modules/**",
 ],
 globals: {
   "ts-jest": {
     tsconfig: "tsconfig.json",
   },
 },
}
```

Y para poder ejecutar los test, editamos los scripts del fichero package.json para añadir:
```json
"test": "jest --coverage"
```

Para probar la ejecución de los test vamos a crear el fichero src/calc.spec.ts con el siguiente contenido:
```typescript
import { Calc } from './calc'

describe('Calc', () => {
 test('should return 10 for add(6, 4)', () => {
   expect(new Calc().add(6, 4)).toBe(10)
 })
 test('should return 9 for add(10, -1)', () => {
   expect(new Calc().add(10, -1)).toBe(9)
 })
})
```

Y ejecutamos:
```bash
$> npm run test
```

## Configuración de ESLint

La integración con ESLint nos permite definir una guía de estilo única en nuestro proyecto que será respetada por todas las personas que trabajen en él. Necesitamos las siguientes dependencias:
```bash
$> npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
```
Ahora para configurar las opciones, tenemos que ejecutar el siguiente comando y contestar a una serie de preguntas:
```bash
$> npx eslint --init
```

- How would you like to use ESLint?
   - To check syntax, find problems, and enforce code style
- What type of modules does your project use?
   - JavaScript modules (import/export)
- Which framework does your project use?
   - None of these
- Does your project use TypeScript?
   - Yes
- Where does your code run? (Seleccionar son la barra espaciadora)
   - Node
- How would you like to define a style for your project?
   - Answer questions about your style
- What format do you want your config file to be in?
   - JavaScript
- What style of indentation do you use?
   - Spaces
- What quotes do you use for strings?
   - Single
- What line endings do you use?
   - Unix
- Do you require semicolons?
   - Yes

Lo que creará un fichero .eslintrc.js en la raíz de proyecto con el siguiente contenido:
```javascript
module.exports = {
   'env': {
       'es2021': true,
       'node': true,
       'jest': true
   },
   'extends': [
       'eslint:recommended',
       'plugin:@typescript-eslint/recommended'
   ],
   'parser': '@typescript-eslint/parser',
   'parserOptions': {
       'ecmaVersion': 12,
       'sourceType': 'module'
   },
   'plugins': [
       '@typescript-eslint'
   ],
   'rules': {
       'indent': [
           'error',
           4
       ],
       'linebreak-style': [
           'error',
           'unix'
       ],
       'quotes': [
           'error',
           'single'
       ],
       'semi': [
           'error',
           'always'
       ]
   }
};
```

> Nota: añadimos jest a env para evitar problemas a la hora de chequear los archivos de test. Y añadimos la siguiente línea a los scripts del package.json:
```bash
"lint": "eslint ./src --ext .ts"
```

Para realizar la integración con el IDE para que nos pueda avisar de los errores y corregirlos al salvar vamos a editar las settings de Visual Studio Code para añadir las siguientes:
```json
/**
   ----- Linting configurations
 */
 // Prevent editor formatting on save for certain file types
 "editor.formatOnSave": true,
 "[javascript]": {
   "editor.formatOnSave": false,
 },
 "[typescript]": {
   "editor.formatOnSave": false,
 },
 // Configure eslint to report + fix errors for correct file types
 "editor.formatOnPaste": true,
 "eslint.autoFixOnSave": true,
 "eslint.validate": [
   "javascript",
   {
     "language": "javascript",
     "autoFix": true
   },
   {
     "language": "typescript",
     "autoFix": true
   }
 ],
```

## Configuración de Husky

Para forzar ciertas acciones a la hora de hacer commit o pushear cambios utilizamos la librería husky, para lo que tenemos que añadir la siguiente dependencia de desarrollo:
```bash
$> npm install --save-dev husky
```

Inicializamos git en el proyecto:
```bash
$> git init
```

Creamos el fichero .gitignore con el siguiente contenido:
```
node_modules/
coverage/
dist/
```

Inicializamos husky:
```bash
$> npx husky install
```

Configuramos husky para ejecutar el script de lint antes de hacer un commit:
```bash
$> npx husky add .husky/pre-commit "npm run lint"
```

Para ejecutar los test antes de hacer push:
```bash
$> npx husky add .husky/pre-push "npm run test"
```

Y para no permitir mensajes de commit que no cumplan con Conventional Commits, para ello vamos a instalar las siguientes dependencias:
```bash
$> 
npm install --save-dev commitlint @commitlint/config-conventional
```

Editamos el fichero package.json para añadir la siguiente sección:
```json
"commitlint": {
 "extends": "@commitlint/config-conventional"
},
```

Y, por último, añadimos el siguiente hook con husky:
```bash
$>  npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
```

## Configuración para debugger

A fin de poder hacer debug con Visual Studio Code necesitamos añadir la propiedad “sourceMap” a true en el fichero tsconfig.json del proyecto, pudiendo quedar de esta forma:

```json
{
"compilerOptions": {
 "target": "es5",
  "module": "commonjs",
  "outDir": "./dist",
  "rootDir": "./src",
  "strict": true,
  "noImplicitAny": true,
  "strictNullChecks": true,
  "esModuleInterop": true,
  "skipLibCheck": true,
  "forceConsistentCasingInFileNames": true,
  "emitDecoratorMetadata": true,
  "experimentalDecorators": true,
  "sourceMap": true
},
"exclude": [
  "node_modules",
  "**/*.spec.ts"
 ]
}
```
y configurar el fichero .vscode/launch.json con el siguiente contenido:
```json
{
   "version": "0.2.0",
   "configurations": [
       {
           "type": "node",
           "request": "launch",
           "name": "Launch Program",
           "program": "${workspaceRoot}/dist/server.js",
           "cwd": "${workspaceRoot}",
           "sourceMaps": true,
           "outFiles": [
               "${workspaceFolder}/dist/**/*.js"
           ]
       }
   ]
}
```
De esta forma podemos poner puntos de ruptura en nuestro código para que la ejecución se detenga y podamos inspeccionar el contexto de ejecución con Visual Studio Code.