Minimalist TypeScript + Node Template
In this post, we detail the setup of a minimal TypeScript project template that we can build upon for other project templates (e.g., Express, GraphQL, MongoDB). Despite being minimal, it sports hot reloading, linting, and code formatting. This has been cobbled together from a variety of resources (see below).
Initializing the Project
Create a directory for the template and initialize a Git repo and a new Node project:
mkdir typescript-node-template
git init
npm init -y
Create a .gitignore
with the following:
dist/
node_modules/
Install some development dependencies:
npm i -D typescript tsc-watch
- typescript compiler
- tsc-watch watches for TypeScript file changes and efficiently recompiles them
Install the ambient types for Node:
npm i -D @types/node
Configuration
Create a default TypeScript config file:
npx tsc --init
Change the following compilerOptions
values from their defaults:
- sourceMap:
false
- no separate source map, since we’re using inline maps (default) - baseUrl:
"."
- default value, but required to be explicitly set whenpaths
is set - paths:
{ "*": ["./src/*"] }
- search for imports within thesrc
directory - outDir:
"dist"
- put all the built files here - typeRoots:
["node_modules/@types"]
- where to explicitly look for type definitions
Add the following entries after the compilerOptions
:
"include": ["node_modules/@types", "src/**/*"],
"exclude": ["node_modules", "dist"]
include
- tells the compiler what to includeexclude
- tells the compiler what to exclude
Code Linting and Formatting
Install the development dependencies for linting:
npm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
- ESLint - the linter itself
- @typescript-eslint/parser - ESLint parser for TypeScript
- @typescript-eslint/eslint-plugin - ESLint rules for TypeScript
Create an .eslintrc.js
file in the project root and add the following:
module.exports = {
parser: "@typescript-eslint/parser", // Specifies the ESLint parser
extends: [
"plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin
],
rules: {
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
// e.g. "@typescript-eslint/explicit-function-return-type": "off",
},
};
Install the development dependencies for formatting:
npm i -D prettier eslint-config-prettier eslint-plugin-prettier
- prettier - the code formatter
- eslint-config-prettier - prevents conflicts between ESLint and Prettier
- eslint-plugin-prettier - runs Prettier as an ESLing rule
Create a .prettierrc.js
and add the following:
module.exports = {
semi: true,
trailingComma: "all",
singleQuote: true,
printWidth: 120,
tabWidth: 2,
};
Along with a .prettierignore
:
node_modules
dist
package-lock.json
*.lock
.gitignore
.prettierignore
Sample Code
Create a src
directory and an index.ts
file in it with the following code:
class Dog {
name: string;
constructor(data: string) {
this.name = data;
}
}
const dog = new Dog("Rover");
if (dog instanceof Dog) {
console.log(`${dog.name} is a dog`);
}
To test, run following command from the terminal:
npm run dev
Hot Reloading
We are using tsc-watch
to provide file watching and incremental recompilation to JavaScript (in dist/
), which we can run the standard node
executable on.
Add the following NPM command to the scripts
section of package.json
:
"dev": "tsc-watch --noClear --onSuccess 'node dist/index.js'"
--noClear
- prevents the console from clearing between recompilations--onSuccess
- if the compilation was successful, then run the resulting JavaScript with Node.js
To test, run following command from the terminal:
npm run dev
and you should see the output: Rover is a dog
.
Try changing the console output and saving. You should see the output in the terminal indicate that it detected a file change and, if there are no errors, your updated ouput.
Building
Add another NPM command to the scripts
of package.json
:
"build": "tsc -b",
To build the project, simply:
npm run build
To test:
node dist/index.js
Final Steps
Create a README.md
, add a LICENSE
, and commit/push! Now we have foundational template we can build on, tweaking and extending it as-needed.