On a big project with a big team it’s hard to maintain consistency in the code style, and sometimes you might do different things when working by yourself.

To prevent code from diverging too much and keep our source code tidy, there are some tools to help:

  • tsconfig.json: the TypeScript configuration file has a few options
  • tslint.json: TSLint allows us to configure rules the code should follow
  • prettier: Prettier is a package that formats code

We use all of these. Here’s how we configure them:

TSConfig

We set 2 options in the TypeScript configuration file:

  • "noUnusedLocals": true
  • "noUnusedParameters": true

The first catches local variables you declare but don’t use, and the second catches unused function/method parameters – both give an error. We find that these configuration options help without getting in our way.
(We don’t set strict or disallow implicit options.)

TSLint

TypeScript Lint (TSLint) is a powerful CLI that allows you to configure rules and then validate your files based on them. Here are ours:

  • "cyclomatic-complexity": [true, 10]
    Prevents functions from growing too complex and too big. Requires package tslint-consistent-codestyle
  • “interface-name”: false
    Enforces TypeScript recommendation not to prefix interfaces with an I
  • “member-access”: [false]
    You’re not required to explicitly say what is public/protected/private
  • “member-ordering”: [ true, { “order”: [“static-field”, “instance-field”, “static-method”, “instance-method”] } ]
    Ensures you put static and instance methods in a certain order
  • “newline-before-return”: true
    Always requires a newline before a return so that you can clearly see exit points
  • “no-bitwise”: false
    Bitwise operations enable checking for even or odd with num & 1 which is very fast, write hash algorithms, and other uses. There is no reason to restrict this, so we turn this off.
  • “no-conditional-assignment”: false
    This also has legitimate uses, when you want to set a variable to one of two values depending on a condition
  • “object-literal-sort-keys”: false
    We don’t require you to sort properties in an object. In many cases it makes more sense to order by context, for example in a person object it makes sense to id, name, email, before address
  • “prefer-object-spread”: true
    This { ...x } is much shorter and easier to read than Object.assign
  • “prefer-template”: true
    We prefer to use Your name is ${name} instead of 'Your name is' + name
  • “early-exit”: true
    Rather than a big if/else in a function, we prefer to have a small if exiting the function early if it cannot continue executing
  • “no-unnecessary-else”: true
    This removes an unnecessary else if you exit on the if, forcing this style:
    if (condition) { return foo; } return bar;
    this makes the logic easier to read.
  • “semicolon”: false
    This was new for us. In some projects we were not using semicolons, so this ensures a more consistent style. It does take getting used to.
  • “only-arrow-functions”: true
    Disallows function(){}. Since functions don’t bind lexical scope, this might not be what you think, so it enforces () => {} to prevent this error.
  • “object-literal-shorthand”: true
    We avoid repetition. If you have a variable name and want to set name of an object you can just do { name } instead of { name: name }
  • “no-default-export”: true
    Default exports are evil. They can be named anything, leading to poor discoverability, they don’t rename with a refactor, and auto-import and intellisense don’t work properly. Instead we use named imports.
  • “no-console”: true
    This rule doesn’t prevent your code from compiling, so we can freely use console.log during development. With that we don’t commit noisy comments to the main branches. If we want to actually log things in production we use a logger system
  • “ordered-imports”: [ true, { “import-sources-order”: “lowercase-last”, “named-imports-order”: “lowercase-first” } ]
    It’s easier to find imports if they are sorted. We use this rule combined with TypeScript Hero (a VSCode extension) to automatically sort imports.

Prettier

Prettier is a code formatter. It is used to create a uniform, consistent formatting for anyone coding in the project. This stops arguments over how code should be formatted, and enables reading other team members’ code without any surprises.

It has a few configuration options. We use:

  • “arrowParens”: “always”
    We always use parentheses on arrow functions, even when there’s only one parameter. This makes them easier to spot.
  • “printWidth”: 120
    With today’s HD or 4K monitors nowadays , there’s no reason not to use some more horizontal space.
  • “singleQuote”: true
    Single quotes are easier to type.
  • “trailingComma”: “all”
    We prefer trailing commas, so on a git diff we won’t see a change to add a comma, and saving time and errors when adding a new element.
  • “semi”: false
    In projects that don’t use semicolons we have this to remove them.

There are three common ways to use prettier:

  • install an extension for your editor (e.g. Visual Code)
  • install the package and run from the CLI
  • configure a post git hook to run prettier CLI on staged files during a commit

To configure a hook all you need is to install these:
npm i -D prettier husky lint-staged

And in your package.json configure add:
"husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "*.{ts,tsx,scss,md,css}": [ "prettier --write", "git add" ] }

This is the setup we have tested at AE Studio.

It helps our team collaborate, and we hope you find it useful, too!

Other Resources: