Linting Typescript Projects

This page will discuss common topics around maintaining the code quality of Typescript codebases using the Mill build tool

Linting and AutoFormatting with Eslint and Prettier

Eslint and Prettier are tools that analyzes your Typescript source code, performing intelligent analyses and code quality checks, and is often able to automatically fix the issues that it discovers. It can also perform automated refactoring.

build.mill (download, browse)
package build

import mill._, javascriptlib._

object foo extends TypeScriptModule with TsLintModule

Mill supports code linting via eslint https://eslint.org out of the box. You can lint your projects code by providing a configuration for eslint and running mill _.checkFormatAll.

You can define npmLintDeps field to add dependencies specific to linting to your module. The task npmLintDeps works the same way as npmDeps or npmDevDeps.

> cat foo/src/foo.ts # initial code with lint errors.
export class Foo{
  static main(
    args: string[
]) {
    console.log(`Hello World`);
  }
}

> mill foo.checkFormatAll # run eslint
...
foo/src/foo.ts
  3:5  error  'args' is defined but never used  @typescript-eslint/no-unused-vars
...
...1 problem (1 error, 0 warnings)

> sed -i.bak 's/Hello World/Hello World ${args.join('\'' '\'')}/' foo/src/foo.ts # fix lint error.

> cat foo/src/foo.ts # initial code with lint errors.
export class Foo{
  static main(
    args: string[
]) {
    console.log(`Hello World ${args.join(' ')}`);
  }
}

> mill foo.checkFormatAll # run eslint
All matched files use Eslint code style!
build.mill (download, browse)
package build

import mill._, javascriptlib._

object foo extends TypeScriptModule with TsLintModule

Mill supports code auto formatting via eslint https://eslint.org and prettier https://prettier.io/docs/en. You can reformat your projects code by providing a configuration for your preferred formtter then running mill _.reformatAll to reformat your code. You can also check for formatting errors by running mill _.checkFormatAll.

If both configuration files are present, the command mill _.checkFormatAll and mill _.reformatAll will default to eslint. If neither files are present, the command will exit with an error, you must include at least one configuration file.

When using prettier you can specify the path to check format or reformat via command line argument, mill _.checkFormatAll "//.ts" just as you would when running prettier --check or mill _.reformatAll "//.ts" just as you would when running prettier --write, if no path is provided mill will default to using "/*/*.ts".

Also if a .prettierignore is not provided, mill will generate one ignoring "node_modules" and ".git".

> cat foo/src/foo.ts # initial poorly formatted source code
export class Foo{
static main(
args: string[
])
{console.log("Hello World!")
}
}

> mill foo.checkFormatAll # run linter - since both eslint and prettier configurations are present, mill will opt to use eslint.
...
foo/src/foo.ts
  2:1   error  Expected indentation of 2 spaces but found 0                                   indent
  3:1   error  Expected indentation of 4 spaces but found 0                                   indent
  5:1   error  Opening curly brace does not appear on the same line as controlling statement  brace-style
  5:1   error  Statement inside of curly braces should be on next line                        brace-style
  5:1   error  Requires a space after '{'                                                     block-spacing
  5:1   error  Expected indentation of 2 spaces but found 0                                   indent
  5:14  error  Strings must use singlequote                                                   quotes
  5:29  error  Missing semicolon                                                              semi
  6:1   error  Expected indentation of 2 spaces but found 0                                   indent
  7:2   error  Newline required at end of file but not found                                  eol-last
...
...10 problems (10 errors, 0 warnings)
...10 errors and 0 warnings potentially fixable with running foo.reformatAll.

> mill foo.reformatAll
...
All matched files have been reformatted!

> cat foo/src/foo.ts # code formatted with eslint configuration.
export class Foo{
  static main(
    args: string[
]) {
    console.log('Hello World!');
  }
}

> rm -rf eslint.config.mjs # since there is no eslint config file `eslint.config.(js|mjs|cjs)`, mill will use the prettier configuration available.

> mill foo.checkFormatAll # run lint with prettier configuration.
Checking formatting...
[warn] foo/src/foo.ts
[warn] Code style issues found. Run foo.reformatAll to fix.

> mill foo.reformatAll
...
All matched files have been reformatted!

> cat foo/src/foo.ts # code formatted with prettier configuration.
export class Foo {
  static main(args: string[]) {
    console.log('Hello World!');
  }
}

Code Coverage with Jest, Mocha, Vitest and Jasmine

Mill supports code coverage analysis with multiple Typescript testing frameworks.

build.mill (download, browse)
package build

import mill._, javascriptlib._

object foo extends TypeScriptModule {
  object test extends TypeScriptTests with TestModule.Jest
}

object bar extends TypeScriptModule {
  object test extends TypeScriptTests with TestModule.Mocha
}

object baz extends TypeScriptModule {
  object test extends TypeScriptTests with TestModule.Vitest
}

object qux extends TypeScriptModule {
  object test extends TypeScriptTests with TestModule.Jasmine
}

Mill supports code coverage with Jest, Mocha, Vitest and Jasmine out of the box. To run a test with coverage, run the command mill _.test.coverage.

The path to generated coverage data can be retrieved with mill _.test.coverageFiles, The path to generated html file can be retrieved with mill _.test.htmlReport.

To use custom configuations for test suites, you can simply include matching test suite config file in your project root.

For custom configurations:

Jest suite expects a jest.config.ts file.

Jasmine suite expects a jasmine.json file.

Mocha suite expects a test-runner.js file.

Vitest suite expects a vitest.config.ts file.

Mocha & Jasmine both rely on nyc https://www.npmjs.com/package/nyc and @istanbuljs/nyc-config-typescript https://www.npmjs.com/package/@istanbuljs/nyc-config-typescript for coverage, when using either, to use custom coverage configurations you must include a .nycrc file in your project root.

Example .nycrc configuration

{
 "extends": "@istanbuljs/nyc-config-typescript",
 "require": ["ts-node/register", "tsconfig-paths/register"],
 "exclude": ["node_modules", "*/**/*.test.ts"],
 "reporter": ["text", "html"],
 ...
}

As always for most use cases you will never need to define a custom test configurtion file.

> mill foo.test.coverage
...Calculator
...
---------------|---------|----------|---------|---------|-------------------
File...| % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s...
---------------|---------|----------|---------|---------|-------------------
...All files...|...62.5 |...50 |...66.66 |...62.5 |...
...calculator.ts...|...62.5 |...50 |...66.66 |...62.5 | 14-17...
---------------|---------|----------|---------|---------|-------------------
...
Test Suites:...1 passed, 1 total...
Tests:...4 passed, 4 total...
...

> mill bar.test.coverage
...
...2 passing...
...
---------------|---------|----------|---------|---------|-------------------
File...| % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s...
---------------|---------|----------|---------|---------|-------------------
...All files...|...66.66 |...0 |...66.66 |...62.5 |...
...calculator.ts...|...66.66 |...0 |...66.66 |...62.5 | 7-10...
---------------|---------|----------|---------|---------|-------------------

> mill baz.test.coverage
.../calculator.test.ts...
...Test Files  1 passed...
...Tests  4 passed...
...
...Coverage report from v8
---------------|---------|----------|---------|---------|-------------------
File...| % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s...
---------------|---------|----------|---------|---------|-------------------
...All files...|...100 |...100 |...100 |...100 |...
...calculator.ts...|...100 |...100 |...100 |...100 |...
---------------|---------|----------|---------|---------|-------------------

> mill qux.test.coverage
...
4 specs, 0 failures
...
---------------|---------|----------|---------|---------|-------------------
File...| % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s...
---------------|---------|----------|---------|---------|-------------------
...All files...|...100 |...100 |...100 |...100 |...
...calculator.ts...|...100 |...100 |...100 |...100 |...
---------------|---------|----------|---------|---------|-------------------