Typescript Linting
Think of linting as your code’s first line of defense, like a spell checker for your syntax and structure. It analyzes source files to identify errors, enforce style guidelines, and highlight patterns that may lead to bugs. By integrating linting into your development workflow or CI pipeline, you create an automated checkpoint that prevents sloppy code from slipping through. This not only improves consistency across the codebase but also reduces review time and debugging later. With linting in place, every commit meets a standard of clarity and correctness before it moves forward.
Here we will be adding linting and code formatting to a nodejs project that includes javascript and typescript files.
We'll start with the Starting with Typescript for CommonJS project.
.
├── .gitignore
├── .nvmrc
├── package.json
├── src
│ └── index.ts
├── tsconfig.json
└── yarn.lock
Adding eslint and prettier for typescript
yarn -D add eslint prettier @eslint/js typescript-eslint eslint-plugin-prettier eslint-config-prettier globals
ESLint
Create an eslint.config.js
import globals from 'globals';
import pluginJs from '@eslint/js';
import tseslint from 'typescript-eslint';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
/** @type {import('eslint').Linter.Config[]} */
export default [
{ files: ['**/*.{js,mjs,cjs,ts}'] },
{ languageOptions: { globals: globals.node } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
eslintPluginPrettierRecommended,
{
rules: {
'no-console': 'warn'
}
},
{
ignores: ['node_modules', 'dist']
}
];
Importing eslint-plugin-prettier/recommended
and adding it as the last item in the configuration array in your eslint.config.js
file so that eslint-config-prettier
has the opportunity to override other configs:
Adding scripts to the package.json
npm pkg set "scripts.lint"="eslint ."
npm pkg set "scripts.lint:fix"="eslint . --fix"
Running yarn lint
will return a number of warnings about using console.log
/Users/mark/src/everydaydevops/typescript-linting-example/src/index.ts
1:1 warning Unexpected console statement no-console
8:3 warning Unexpected console statement no-console
✖ 2 problems (0 errors, 2 warnings)
✨ Done in 1.79s.
These can be fixed by adding the following to each of the console.log
lines
// eslint-disable-line no-console
Prettier
Create a prettier config and ignore file
cat << EOF > .prettierrc
{
"semi": true,
"trailingComma": "none",
"singleQuote": true,
"printWidth": 80
}
EOF
cat << EOF > .prettierignore
node_modules
dist
.build
EOF
Adding scripts for prettier
npm pkg set "scripts.prettier"="prettier . --check"
npm pkg set "scripts.prettier:fix"="prettier . --write"
Running yarn prettier
will also show a few warnings
Checking formatting...
[warn] tsconfig.json
[warn] Code style issues found in the above file. Run Prettier with --write to fix.
Commit and Fix the Code
git add .
git commit -m "Added eslint and prettier for static analysis best practices"
Now we can fix the code
yarn lint:fix
yarn prettier:fix
Protecting Git with husky & lint-staged
Adding husky will make sure that linting is run before any commit and return an error if linting doesn't pass. This helps with keeping git clean without having to run github actions on code that will fail linting.
Add husky and initialize it.
yarn add -D husky
npx husky init
Out of the box husky will run the test
script so if you don't have one you can add it with
npm pkg set "scripts.test"="echo no tests"
Now we need to run husky when specific files are commit to git. For this we'll use lint-staged
yarn add -D lint-staged tsc-files
Then adding directives to package.json
on what to run when specific files are committed.
npm pkg set "lint-staged[**/*.js][0]"="prettier --write"
npm pkg set "lint-staged[**/*.js][1]"="eslint --fix"
npm pkg set "lint-staged[**/*.ts][0]"="tsc-files --noEmit"
npm pkg set "lint-staged[**/*.ts][1]"="prettier --write"
npm pkg set "lint-staged[**/*.ts][2]"="eslint --fix"
npm pkg set "lint-staged[**/*.{json,md,yaml,yml}][0]"="prettier --write"
The lint-staged
section of package.json
looks like
"lint-staged": {
"**/*.js": [
"prettier --write",
"eslint --fix"
],
"**/*.ts": [
"tsc-files --noEmit",
"prettier --write",
"eslint --fix"
],
"**/*.{json,md,yaml,yml}": [
"prettier --write"
]
}
Now adding this to husky so that its run on each commit
echo "npx lint-staged" >> .husky/pre-commit
Adding all the new files and committing to git will run the linting.
git add .
git commit -m "Added husky and lint-staged to run pre-commit hooks on check-in"
This shows that the test script was run and the lint-staged for the matching files
no tests
✔ Backed up original state in git stash (8d9bf2c)
✔ Running tasks for staged files...
✔ Applying modifications from tasks...
✔ Cleaning up temporary files...
If there was a linting error then the files would not be committed to git.
Github Action
Adding a github action makes sure that the linting is run on each pull-request and will return an error if something isn't right:
Create a new file .github/workflows/lint.yaml
name: Lint
on:
pull_request:
branches:
- main
jobs:
run-linters:
name: Run linters
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22.x
- name: Install Node.js dependencies
run: yarn --frozen-lockfile
- name: Run linters
run: yarn lint
If I create a new branch for this:
git checkout -b chore/adding_github_action_lint
git add .
git commit -m "Adding github action to run linting on all PRs" -a
git push --set-upstream origin chore/adding_github_action_lint
Now, creating a new PR in github will run the linting.

Now I can safely merge this PR to main, knowing that it passes linting.
I've shared this project on github as an example: https://github.com/markcallen/typescript-linting-example