Search justacoding.blog. [Enter] to search. Click anywhere to close.

January 13th, 2023

Create A Node TypeScript Starter Project With Tests And Prettifying

There are many “starter” project templates out there for basically any kind of stack or setup. These are simply boilerplate projects that provide you with the basic setup/configuration required based upon your chosen framework or tool.

But let’s walk through how to create your own boilerplate, instead.

What are we going to create?

Specifically, we’ll create a Node starter project that is integrated with TypeScript, Prettier and Jest for testing.

The idea is that you’ll be able to build your own projects on top of this bare-bones template, where all of the underlying configuration and setup has already been implemented.

I’ll explain each part of the process, so you can tweak the starter template to suit your own needs as required.

With that said, let’s begin!

Create a new directory

First thing’s first, we’ll need a new directory for our project to Node/TypeScript boilerplate project to live in.

Create it via the terminal, in a location of your choice, with:

mkdir node-typescript-template

Or simply create the directory manually.

Initialise npm

npm is a package manager, we’ll use it to install all of the packages we’ll need within our new starter project (also known as dependencies).

From your project’s directory, run:

npm init -y

You’ll notice that this command generates a new package.json file within your project.

The role of package.json

This file contains basic information about your project, as well as listing the relevant dependencies, too.

If another developer wants to set up your project later on, for example, he or she will need access to the same dependencies that you’ve installed (we’ll do that soon). They can do so by running npm install, as you may be familiar with.

That’s one of the primary uses of package.json; tracking a project’s dependencies so that other developers can easily install these packages in their own environment. The packages shouldn’t necessarily exist in version control, but only be referenced in the package.json file.

To read more about the package.json file and the many options and configurations that can be used, do be sure to check out the official package.json docs.

Why can’t I just include the packages in my project/in version control?

You can, technically. It’s just not a good idea nor is it standard practice to do so.

This directory of installed dependences (node_modules) can be come rather large and unwieldy.

Regardless, package.json knows exactly what packages & their versions should be installed, so there’s really no benefit to including the dependencies directly in your project (or in version control) anyway.

Install and configure TypeScript

Now we’ve set up npm, we have everything we need to begin actually installing our required dependencies. As we are building a Node/TypeScript starter project (or template) here, the first dependency we’ll want to install is TypeScript itself:

npm install typescript --save-dev

This command simply installs TypeScript for us, and adds an entry to package.json that details this dependency specifically. Subsequent executions of npm install will fetch the same package and version as per the entry created in this file.

The purpose of TypeScript

We won’t go into explicit detail here, but in general the purpose of TypeScript is to promote cleaner and more robust code. It does this via implementing a type system, so that you can’t assign strings to numeric variables or vice-versa.

TypeScript compiles your code, this compilation will fail if there are errors such as mismatching types (it’ll also highlight other JavaScript problems, not just things related to types).

This is a very, very basic summary – do be sure to scan the official TypeScript introduction for a more thorough overview.

With that in mind, let’s look at configuring TypeScript to behave how we’d like.

Configuring TypeScript

To control the behaviour of TypeScript, we’ll need a file named tsconfig.json in the root of the starter project:

touch tsconfig.json

This is a simple JSON file, and inside of it we’ll specify a set of rules and configurations for TypeScript to act upon.

So it’s a simple configuration file, essentially — we’ll use a lot of these configuration files in general when setting up applications and build processes such as this one.

Here’s a basic tsconfig.json file for you to use:

{
    "compilerOptions": {
        "module": "commonjs",
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "target": "es6",
        "noImplicitAny": true,
        "moduleResolution": "node",
        "sourceMap": true,
        "outDir": "dist",
        "baseUrl": ".",
        "paths": {
            "*": ["node_modules/*", "src/types/*"]
        }
    },
    "include": ["src/**/*"]
}

There are many options that can be included in this file.

I want go into them in detail in this article, but be sure to give the official tsconfig.json documentation a look if you’re interested.

At this point, the TypeScript configuration we have added is sufficient for us to proceed to the next step, adding our first TypeScript file to the boilerplate project.

Add the first TypeScript file and test compilation

Let’s create a new TypeScript file in a new directory:

mkdir src
cd src
touch index.ts

As you can see, this is a TypeScript file (it ends in the .ts extension), thus the TypeScript compiler knows to act upon this file.

When we initiate the TypeScript functionality (which we’ll do soon), we’d expect some output – specifically, .js files in our dist directory (as dictated by the outDir property in our tsconfig.json file above).

TypeScript is taking these .ts files and building out some raw JavaScript files for us (this is what’s known as transpiling).

As per the overall point or purpose of TypeScript, it should verify these files, too – to ensure that there are no issues or problems with the types contained therein (amongst other things).

Let’s give that a test to see it working in practice.

Actually run (or compile) the TypeScript

To run TypeScript and generate our assets, you can use this command:

npx tsc

tsc is a TypeScript compiler.

You’ll see a dist directory created now. This contains the JavaScript that has been compiled/transpiled for us, based upon the TypeScript files in our project (only index.ts for now).

This is the standard procedure if the files were deemed to be problem free. TypeScript didn’t detect any issues, so it’s compiled the JavaScript for us successfully.

But what about if the TypeScript file(s) contain issues?

Let’s test that, too.

Forcing a TypeScript compilation error

Update index.ts to include something like:

const age: number = "14"

So this is blatantly not correct.

We’ve got a variable of type number, but it’s been assigned a string as it’s value.

TypeScript won’t allow this, so you’ll notice two things:

  1. It’ll likely be highlighted in your IDE or editor that this line is problematic
  2. When you run TypeScript again, it will complain and the build won’t succeed

To put this to the test, attempt to build again with npx tsc and observe the results in the terminal.

TypeScript will capture that specific error for us, and thus prevent compilation.

We’d just need to fix the problem and rebuild.

With this in mind, TypeScript has now been integrated into our starter project, and it seems to be functioning as we require. Great!

Let’s move on and look at some improvements we can make to this process.

Make TypeScript run automatically

You may notice that it’s quite cumbersome to run npx tsc every time you make a change. We really don’t want to have to manually compile after every change.

Thankfully, there’s an easy way around this.

We can install and configure some dependencies to automatically reload and regenerate our assets every time a file has changed. This clearly makes development a much quicker and a more enjoyable experience!

Installing the required dependencies for automatic reloading/compilation

We’ll need to implement nodemon and ts-node into our starter project setup to achieve this.

nodemon is a simple monitoring tool, it’ll effectively “watch” your files and restart your node application automatically any time one of these relevant files is modified.

ts-node performs a similar (but not equal) role to the TypeScript compiler we’ve used previously (tsc). You can read about the various differences between the two in this StackOverflow question: What’s the difference between tsc (TypeScript compiler) and ts-node?

With that said, let’s install these two dependencies:

npm install --save-dev ts-node nodemon

After installing these new dependencies, we’ll need another config file – this time, nodemon.json.

touch nodemon.json

A simple nodemon.json config you can use is this one:

{
    "watch": ["src"],
    "ext": ".ts,.js",
    "ignore": [],
    "exec": "npx ts-node ./src/index.ts"
}

Like with the tsconfig.json file previously; this file simply instructs nodemon on how to function and configures its behaviour.

Now you can run nodemon with:

npx nodemon

So what’s happening here?

How does nodemon work in conjunction with ts-node?

If you observe the terminal after running npx nodemon, you’ll notice it’s begins to “watch” your files.

When one of these files is changed, nodemon “does something” — in this case, it uses ts-node to build the relevant output into the dist directory..

So this stuff happens automatically, in the background, as you begin to develop and build up the .ts files within your project.

Neat!

To test this, simply change one of the files (index.ts) and observe nodemon doing it’s thing.

You’ll see that the assets in the dist directory are regenerated once more, after each and every change. Errors in your files will be present in your IDE but also in the terminal output, and the build process won’t complete.

To summarise so far

So we’re at a point now where we have a fully-functional Node/TypeScript starter template set up.

We can add and edit TypeScript files and let nodemon handle the compilation for us.

The only command required is this npx nodemon – to start nodemon itself.

Then we can focus solely on development, knowing we have TypeScript in place to really help us produce some robust code.

So this starter template/boilerplate project is technically in a position where it can be considered somewhat “complete”, however, there are a few nice-to-haves which we should definitely implement (not to mention, a testing setup).

So let’s go over that stuff, too.

Install and configure a code prettifier

It’s good to be picky about how your code “looks” and how it’s formatted; it’s also good to be consistent.

That’s where a prettifier or beautifier comes in, like prettier.

What is prettier?

The role of prettier is to “prettify” your code as per a specific configuration.

So prettier will remove semi-colons, it’ll covert double quotes to single quotes and so on. It can be tailored to fit exactly to your requirements, and there are many, many different configuration options along with the examples listed here.

The idea is that all other developers will adhere to this configuration file, and it’ll lead to a more neatly-formatted, consistent codebase.

Configuring prettier

Let’s install prettier with:

npm install --save-dev --save-exact prettier

And again, as you may have guessed, we’ll need a 3rd configuration file. In this case, named .prettierrc:

touch .prettierrc

As mentioned prior, there are many options in terms of configuring prettier. But for now, here’s a basic setup to get your started:

{
    "trailingComma": "es5",
    "tabWidth": 4,
    "semi": false,
    "singleQuote": true
}

And that concludes the prettier configuration for now.

To actually test the prettifying functionality, you can do this:

npx prettier --write .

Firstly change the index.ts file to include some things you don’t want, ie. a semi-colon and/or double quotes.

Now run the above command, and observe how prettier formats this file for us.

Neat, huh?

Set up your editor to work with prettier

Prettier is integrated into most IDEs, so the prettifying can take place either on the fly or via an option in the IDE.

There’s extensive documentation for this based on your specific IDE of choice, see: Editor Integration for Prettier.

For my IDE of choice, Visual Studio Code, formatting an individual file is as simple as running the “Format Document” command directly.

Set up the testing infrastructure

This Node/TypeScript starter template wouldn’t be complete without the necessary testing configuration and setup.

Any type of project will ideally want some test coverage, so let’s look at installing and configuring the dependencies we’ll need for this.

In this case, it would be good to use a testing library known as Jest, so we’ll integrate this tool into out boilerplate project.

Adding Jest

Firstly, install jest and ts-jest with:

npm install jest @types/jest ts-jest -D

And then add another config file, jest.config.js:

module.exports = {
  "roots": [
    "<rootDir>/src"
  ],
  "testMatch": [
    "**/__tests__/**/*.+(ts|tsx|js)",
    "**/?(*.)+(spec|test).+(ts|tsx|js)"
  ],
  "transform": {
    "^.+\\.(ts|tsx)$": "ts-jest"
  },
}

For now, that’s the only jest configuration we’ll need.

The configuration here is just telling jest where to look for test files specifically. It’s also configuring ts-jest to allow us to test TypeScript files (it’s role is to transform the TypeScript files to a usable format ie. .js files).

Now add a simple test, such as index.test.js (this naming convention, ending in .test.js, matches the pattern we’ve stipulated in our jest config file, under the testMatch property).

And a simple test case using Jest:

test("my first test", () => {
    expect(1).toBe(1)
})

Now if you actually run jest, you should see some output in the terminal informing you that the test has passed.

You can run jest with:

npx jest

If you tweak the test to force it to fail, ie. use expect(1).toBe(2), you’ll see that Jest will notify you of this failure and point to the problematic part of the test.

Great, so Jest is functional and it’s integrated with TypeScript, which is what we want!

And for now, that concludes the initial part of our template setup. Let’s summarise where we are at.

Where we are at now

To recap the Node/TypeScript boilerplate template so far, we’ve covered these basic areas:

  • We’ve set up a new project
  • Initialised npm
  • Installed and configured TypeScript
  • Installed Node
  • Installed and configured prettier, tested prettification of our files
  • Installed and configured jest, along with a basic test

With this in mind, the template is sufficient for us to build upon at this point.

We’ve got TypeScript integration, we can prettify our code to keep it clean, and we’ve got a basic testing infrastructure in place, too.

Check back later…

I hope you’ve enjoyed this article.

Please do check back later as we’ll be making further additions to this Node/TypeScript starter template (or project) including the implementation of a linter and the Express framework.

Thanks for reading!

Have any questions? Ping me over at @justacodingblog
← Back to blog