Zac Fukuda
048

ESLint, TypeScript, and Prettier Config Files
How to setup the modern frontend project

It is easy to get started with the front-end development. Many libraries provide the starter kits. You type one command, execute, and boom. The project is ready.

Yet, when the modification is necessary, things are just so interwoven. So much headache. So much waste of time going through the doc to doc. You have to solve the puzzle.

Here is my personal solution to that puzzle. The puzzle comprising ESLint, TypeScript, and Prettier configs.

When I start a new project or add modification to the ongoing project, I use the following config files as a starting point.

TypeScript

TypeScript Logo

The Official Documentation

tsconfig.json
{
	"compilerOptions": {
		"allowJs": false,
		"allowSyntheticDefaultImports": false,
		"esModuleInterop": false,
		"forceConsistentCasingInFileNames": true,
		"isolatedModules": false,
		"jsx": "react",
		"lib": ["DOM", "ES2018"],
		"module": "ESNext",
		"moduleResolution": "Node",
		"noEmit": false,
		"outDir": "./dist/",
		"resolveJsonModule": false,
		"skipLibCheck": true,
		"strict": true,
		"target": "ESNext",
		"useDefineForClassFields": true
	},
	"include": ["src/ts/**/*"]
}

One thing to note is that I set "noEmit": false. This is because I manage the build task with Gulp, which uses Webpack and ts-loader. In many cases you simply use TypeScript or other bundlers with the TypeScript plugin for transpilation. In which case "noEmit": true is the right configuration.

For more information on tsconfig, see Intro to the TSConfig Reference.

Bundlers

You may need to configure based on the bundler you use:

Prettier

Prettier Logo

The Official Documentation

.prettierrc
{
	"printWidth": 80,
	"tabWidth": 2,
	"semi": false,
	"useTabs": true,
	"singleQuote": true,
	"jsxSingleQuote": false,
	"trailingComma": "es5",
	"bracketSpacing": true,
	"bracketSameLine": false,
	"arrowParens": "always",
	"proseWrap": "never",
	"htmlWhitespaceSensitivity": "css",
	"vueIndentScriptAndStyle": false,
	"endOfLine": "lf",
	"embeddedLanguageFormatting": "auto",
	"singleAttributePerLine": false,
	"trailingCommaPHP": true,
	"overrides": [
		{
			"files": "*.html",
			"options": {
				"printWidth": 999
			}
		},
		{
			"files": ["*.json"],
			"options": {
				"tabWidth": 4,
				"useTabs": false
			}
		},
		{
			"files": "*.php",
			"options": {
				"braceStyle": "1tbs"
			}
		}
	]
}

I prefer tabs over spaces, like Richard Hendricks, with the corresponding indent size to two spaces.

I don’t like HTML tags being wrapped into multi-line for each attribute. To prevent this, I set "printWidth": 999 for HTML files.

PHP

In order to make Prettier effective on PHP files, I install the additional package @prettier/plugin-php:

# npm
npm install --save-dev @prettier/plugin-php
# yarn
yarn add --dev prettier @prettier/plugin-php
# pnpm
pnpm add --save-dev@prettier/plugin-php

Ignore Files

Here is also my .prettierignore file. It would be better to exclude the files that you don’t code or modified by the tools.

.prettierignore
/build
/dist
/font
/img
/node_modules
/vendor
.env
package.json

ESLint

ESLint Logo

The Official Documentation

You need a bit of work to integrate ESLint with TypeScript and Prettier.

ESLint–TypeScript Package

Ironically, Microsoft does not provide the ESLint plugin for their tool. Luckily we have a community backup. It’s typescript-eslint.

# npm
npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint typescript
# yarn
yarn add --dev @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint typescript
# pnpm
pnpm add --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint typescript

ESLint–Prettier Package

Prettier does have its own ESLint plugin eslint-config-prettier.

#npm
npm install --save-dev eslint-config-prettier
# yarn
yarn add --dev eslint-config-prettier
#pnpm
pnpm add --save-dev eslint-config-prettier

Config

As of September 2023, ESLint is amid of updating their config scheme. The name of the new default config file is eslint.config.js. However, typescript-eslint does not yet support the new scheme. So we are going to stick to the deprecating .eslintrc.js for a while.

.eslintrc.js
/* eslint-env node */
module.exports = {
	env: {
		browser: true,
		es2021: true,
	},
	extends: [
		'eslint:recommended',
		'plugin:@typescript-eslint/recommended',
		'prettier',
	],
	globals: {
		axios: 'readonly',
		jQuery: 'readonly',
	},
	overrides: [],
	parser: '@typescript-eslint/parser',
	parserOptions: {
		ecmaVersion: 'latest',
		sourceType: 'module',
	},
	plugins: ['@typescript-eslint'],
	rules: {
		'no-unused-vars': 'off', // Required by @typescript-eslint/no-unused-vars
		'padding-line-between-statements': [
			'error',
			{ blankLine: 'always', prev: ['class', 'function'], next: '*' },
			{
				blankLine: 'always',
				prev: '*',
				next: ['class', 'function', 'return', 'try'],
			},
			{
				blankLine: 'always',
				prev: ['import', 'for', 'if', 'switch', 'while'],
				next: ['const', 'expression', 'let'],
			},
			{
				blankLine: 'always',
				prev: ['const', 'expression', 'let'],
				next: ['for', 'if', 'switch', 'try', 'while'],
			},
			{
				blankLine: 'always',
				prev: ['const', 'expression', 'import', 'let'],
				next: 'export',
			},
			{ blankLine: 'always', prev: ['for', 'switch', 'while'], next: 'if' },
			{ blankLine: 'always', prev: ['if', 'switch', 'while'], next: 'for' },
			{ blankLine: 'always', prev: ['if', 'for', 'while'], next: 'switch' },
			{ blankLine: 'always', prev: ['if', 'for', 'switch'], next: 'while' },
		],
		/* @typescript-eslint */
		'@typescript-eslint/consistent-type-imports': [
			'error',
			{
				prefer: 'type-imports',
				disallowTypeAnnotations: true,
				fixStyle: 'separate-type-imports',
			},
		],
		'@typescript-eslint/no-unused-vars': [
			'error',
			{ vars: 'all', args: 'all' },
		],
	},
	root: true,
}

Integration with Front-End Libraries

If you use one of the following modern front-end libraries, you might need the additional configuration.

Bonus

As a bonus content, I present my personal editor config and VS Code setting files.

Editor Config

.editorconfig
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = tab
indent_size = 2
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.json]
indent_style = space
indent_size = 4

VS Code

.vscode/settings.json
{
	"editor.codeActionsOnSave": {
			"source.fixAll.eslint": true
	},
	"editor.defaultFormatter": "esbenp.prettier-vscode",
	"editor.formatOnSave": true,
	"editor.tabSize": 2,
	"editor.insertSpaces": false,
	"editor.detectIndentation": false,
	"eslint.enable": true
}