How we keep our code shining and uniform with TypeScript configurations and Prettier
To prevent code from diverging too much and keep our source code tidy, here are some tools and configuration settings we use.
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 useYour 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:
- Example of Prettier hook on create-react-app repo
- Export default is bad by basarat
- Join AE Studio
No one works with an agency just because they have a clever blog. To work with my colleagues, who spend their days developing software that turns your MVP into an IPO, rather than writing blog posts, click here (Then you can spend your time reading our content from your yacht / pied-a-terre). If you can’t afford to build an app, you can always learn how to succeed in tech by reading other essays.
How we keep our code shining and uniform with TypeScript configurations and Prettier
To prevent code from diverging too much and keep our source code tidy, here are some tools and configuration settings we use.
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 useYour 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:
- Example of Prettier hook on create-react-app repo
- Export default is bad by basarat
- Join AE Studio