Nếu bạn là một lập trình viên JavaScript, chắc chắn bạn đã từng đối mặt với những lỗi nhỏ nhặt nhưng khó chịu như dấu chấm phẩy thiếu, thụt lề không đúng, hoặc những đoạn mã khó đọc do không tuân thủ quy chuẩn nào cả.
Để giải quyết những vấn đề này một cách hiệu quả và chuyên nghiệp, ESLint chính là công cụ hỗ trợ hoàn hảo. Trong bài viết này, chúng ta sẽ cùng tìm hiểu cách ESLint hoạt động, tại sao bạn nên sử dụng nó, và làm thế nào để tích hợp ESLint vào dự án của mình để tối ưu hóa quy trình phát triển phần mềm, đạt được "clean code" trong dự án.
1. ESLint là gì?
ESLint là công cụ phân tích code JavaScript và TypeScript, nhằm phát hiện các lỗi cú pháp, lỗi logic, đảm bảm code tuân thủ quy tắc được thiết lập trước. ESLint được xây dựng với mục đích:
- Tự động phát hiện lỗi trong quá trình phát triển.
- Đảm bảo tính nhất quán trong code của các dự án lớn.
Với ESLint bạn hoàn toàn có thể tạo ra các rule của riêng mình, hoặc sử dụng các bộ quy tắc được cộng đồng phát triển và chia sẻ.
2. Ưu và Nhược điểm của ESLint
2.1 Ưu điểm của ESLint
- Phát hiện lỗi sớm: ESLint giúp phát hiện lỗi cú pháp và logic sớm trong quá trình viết code, giảm thiểu lỗi runtime.
- Tính nhất quán, clean code: bằng việc tạo ra các quy tắc cho code, đảm bảo tất cả các thành viên trong team tuân thủ, làm cho code sạch.
- Khả năng tùy chỉnh: bạn có thể tạo và sử dụng các rule, tích hợp plugin hoặc thiết lập bộ quy tắc riêng.
- Dễ dàng tích hợp với IDE, công cụ CI/CD: ESLint dễ dàng tích hợp với Visual Studio Code, Atom, và các công cụ CI/CD như Jenkins, GitHub Actions.
- Hỗ trợ TypeScript: với
@typescript-eslint
plugin, ESLint có thể làm việc tốt trong các dự án TypeScript.
2.2 Nhược điểm của ESLint
- Cấu hình phức tạp: để tận dụng tối đa khả năng của ESLint, việc cấu hình bạn sẽ cảm thấy khá phức tạp, đặc biệt với dự án lớn cần có nhiều quy tắc.
- Hiệu suất: nếu không được cấu hình đúng cách, ESLint có thể làm chậm quá trình viết code, đặc biệt khi chạy trên tệp lớn hoặc có nhiều quy tắc kiểm tra.
- Xung đột với công cụ khác: Nếu bạn không cấu hình phù hợp thì một vài rule của ESLint có thể xung đột với Prettier.
3. Các Plugin và Rule hữu ích của ESLint
3.1 Plugins
3.1.1 eslint-plugin-import
Plugin này giúp bạn kiểm tra tính hợp lệ của câu lệnh import, đảm bảo các module được import tồn tại và được đặt đúng thứ tự.
import/no-unresolved
: phát hiện khi import các module không tồn tại.import/order
: phải sắp xếp các import theo một thứ tự cụ thể (ví dụ: import của bên thứ ba, sau đó đến import các module nội bộ).
{
"plugins": ["import"],
"rules": {
"import/no-unresolved": "error",
"import/order": [
"error",
{
"groups": ["builtin", "external", "internal"],
"alphabetize": { "order": "asc", "caseInsensitive": true }
}
]
}
}
3.1.2 eslint-plugin-react
Plugin này hỗ trợ bạn trong việc phát triển các dự án React, giúp bạn phát hiện các vấn đề trong code React.
react/jsx-uses-vars
: phát hiện các biến đã khai báo nhưng chưa được sử dụng trong JSX.react/jsx-uses-react
: đảm bảo rằngReact
được khai báo trước khi sử dụng. Nhưng từ React 17 trở đi thì bạn không cần phải khai báoReact
nữa, nhưng từ React 16 trở về trước thì phải khai báo trong mỗi file JSX để hoạt động.
{
"plugins": ["react"],
"rules": {
"react/jsx-uses-react": "error",
//nếu bạn không sử dụng bạn có thể để off thay vì error
"react/jsx-uses-vars": "error"
}
}
3.1.3 @typescript-eslint/eslint-plugin
@typescript-eslint/explicit-function-return-type
: yêu cầu phải khai báo kiểu dữ liệu trả về của tất cả các hàm.
//đúng quy tắc
function add(a: number, b: number): number {
return a + b;
}
//không có kiểu dữ liệu hàm trả về
function add(a: number, b: number) {
return a + b;
}
@typescript-eslint/no-unused-vars
: phát hiện các biến hoặc tham số của hàm không được sử dụng trong code.
function greet(name: string, age: number): void {
console.log(`Hello, ${name}!`);
// 'age' không được sử dụng
}
{
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/explicit-function-return-type": "warn",
"@typescript-eslint/no-unused-vars": "error"
}
}
3.1.4 eslint-plugin-jsx-a11y
jsx-a11y/anchor-is-valid
: đảm bảo các thẻ<a>
có thuộc tính href hợp lệ.jsx-a11y/alt-text
: đảm bảo các thẻ<img>
có thuộc tínhalt
để cải thiện accessibility.
{
"plugins": ["jsx-a11y"],
"rules": {
"jsx-a11y/anchor-is-valid": "warn",
"jsx-a11y/alt-text": "error"
}
}
3.1.5 eslint-plugin-prettier
ESLint sẽ kết hợp với Prettier để định dạng mã nguồn tự động theo cấu hình của Prettie.prettier/prettier
: dùng quy tắc của Prettier để kiểm tra lỗi format trong code.
Cách cấu hình trong ESLint:
{
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "warn"
}
}
3.2 Các quy tắc (rules) của ESLint
3.2.1 Quy tắc về tính nhất quán
indent
: đảm bảo code được tab (canh lề) đồng nhất với nhau, bình thường mình sẽ sử dụng là 2, bạn có thể dùng 4 cũng được nhé.
{
"rules": {
"indent": ["error", 2]
}
}
quotes
: bắt buộc phải sử dụng dấu nháy đôi"
hoặc là nháy đơn'
, mình thì thường sử dụng'
hơn
{
"rules": {
"quotes": ["error", "single"]
}
}
semi
: bắt buộc không hoặc có dấu chấm phẩy ở cuối câu lệnh code, mình cũng thường dùng;
ở cuối câu lệnh hơn.
{
"rules": {
"semi": ["error", "always"]
}
}
3.2.2 Quy tắc về chất lượng mã
no-console
: ngăn việc sử dụngconsole.log
trong mã nguồn, mình thấy các bạn thường sử dụng để log xem dữ liệu trả về, ok rồi thì nên xoá đi nhé.
{
"rules": {
"no-console": "warn"
}
}
eqeqeq
: bạn phải sử dụng toán tử so sánh nghiêm ngặt hơn===
thay vì==
{
"rules": {
"eqeqeq": "error"
}
}
3.2.3 Quy tắc để tránh các lỗi tiềm ẩn
no-undef
: ngăn chặn việc sử dụng các biến chưa được định nghĩa
{
"rules": {
"no-undef": "error"
}
}
no-unused-vars
: phát hiện và cảnh báo các biến chưa được sử dụng
{
"rules": {
"no-unused-vars": ["warn", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }]
}
}
3.2.4 Quy tắc cho các dự án Typescript
@typescript-eslint/no-explicit-any
: như các bạn đã biết, nếu bạn đặt kiểuany
thì code đó sẽ là Javascript, không còn rõ ràng về kiểu dữ liệu nữa và điều đó không nên, ngăn chặn việc sử dụng kiểuany
.
{
"rules": {
"@typescript-eslint/no-explicit-any": "error"
}
}
4. Hướng dẫn cấu hình ESLint cho dự án Typescript
4.1 Khởi tạo dự án Typescript
npm init -y
npm install ts-node typescript --save-dev
npx tsc --init
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"jsx": "react",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": ["src"],
"exclude": ["node_modules"]
}
4.2 Cài đặt ESLint cho dự án
npm i eslint --save-dev
npx eslint --init
Khi bạn chạy lệnh khởi tạo file cấu hình ESLint sẽ cần bạn trả lời một số câu hỏi:
- How would you like to use ESLint? - Chọn "To check syntax, find problems".
- What type of modules does your project use? - Chọn "JavaScript modules (import/export)".
- Which framework does your project use? - Chọn "None of these".
- Does your project use TypeScript? - Chọn "Yes".
- Where does your code run? - Chọn "Node".
- Would you like to install them now? - Chọn "Yes"
- Which package manager do you want to use? - Chọn "npm"
ESLint sẽ cài đặt các phụ thuộc cần thiết và tạo ra file eslint.config.mjs
trong thư mục gốc dự án của bạn.
Mình nghĩ nên cài đặt các plugin này để ESLint có thể hoạt động tốt với Typescript, cải thiện chất lượng code,...
npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-import eslint-plugin-prettier eslint-config-prettier @types/react @types/react-dom --save-dev
Tại khối script trong file package.json
bạn thêm đoạn sau để chạy ESLint kiểm tra và tự động fix lỗi:
"scripts": {
"lint": "eslint 'src/**/*.{js,ts,tsx}'",
"lint:fix": "eslint 'src/**/*.{js,ts,tsx}' --fix",
}
import eslintPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import importPlugin from 'eslint-plugin-import';
import prettierPlugin from 'eslint-plugin-prettier';
export default [
{
ignores: [
'**/node_modules/**',
'**/dist/**',
'**/build/**',
],
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json',
tsconfigRootDir: process.cwd(),
},
},
plugins: {
'@typescript-eslint': eslintPlugin,
import: importPlugin,
prettier: prettierPlugin,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:prettier/recommended',
'prettier',
],
rules: {
'prettier/prettier': 'error',
'import/no-unresolved': 'error',
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal'],
alphabetize: { order: 'asc', caseInsensitive: true },
},
],
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/explicit-function-return-type': 'warn',
'@typescript-eslint/no-explicit-any': 'error',
},
},
];
Cuối cùng, bạn có thể chạy câu lệnh npm run lint
để kiểm tra các vấn đề tồn tại trong code, nếu bạn muốn tự động sửa lỗi (lưu ý là ESLint chỉ có thể fix giúp bạn một vài lỗi thôi nhé) thì chạy lệnh npm run lint:fix
Mình để một ví dụ đơn giản như sau:
- src/index.ts
console.log("Hello, 200Lab");
5. Những lỗi thường gặp phải khi dùng ESLint
5.1 "Parsing error: Cannot find module '@typescript-eslint/parser'"
Khi bạn gặp lỗi này hãy thử kiểm tra xem đã cài đặt @typescript-eslint/parser
và cấu hình đúng trong .eslintrc.json
chưa.
- Đây là lệnh cài đặt
npm install --save-dev @typescript-eslint/parser
- Đảm bảo đường dẫn đến tsconfig.json là đúng
5.2 "Cannot read file 'tsconfig.json'."
Lỗi này mình thấy khá là phổ biến, đa số các bạn hay gặp phải, lỗi này do ESLint không tìm thấy hoặc không thể đọc file tsconfig.json
.
Khi bạn gặp lỗi này có thể là do:
- Sự tồn tại của file
tsconfig.json
- Đường dẫn đến file
tsconfig.json
không chính xác. - Bị lỗi cú pháp trong file
tsconfig.json
5.3 Lỗi liên quan đến import
và export
5.3.1 Lỗi import/no-unresolved
Lỗi này xảy ra khi ESLint không tìm thấy module được import, có thể bạn chưa cài đặt module, cấu hình sai paths
trong tsconfig.json
Bạn hãy thử kiểm tra:
- Bạn đã cài đặt module đó chưa?
npm install <module-name>
- Bạn cần đảm bảo bạn đã import với đường dẫn chính xác
- Hãy thử kiểm tra
paths
vàbaseUrl
trong filetsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@components/*": ["src/components/*"]
}
}
}
5.3.2 Lỗi import/extensions
ESLint sẽ cảnh báo nếu bạn cố gắng import một module mà không chỉ định phần mở rộng. Một số cách khắc phục lỗi này:
- Thêm phần mở rộng của tệp:
.js
,.jsx
,.ts
, hoặc.tsx
khi import
import MyComponent from './MyComponent.ts';
- Bạn có thể vào file
.eslintrc.json
cấu hình ESLint để bỏ qua yêu cầu:
{
"rules": {
"import/extensions": [
"error",
"ignorePackages",
{
"js": "never",
"jsx": "never",
"ts": "never",
"tsx": "never"
}
]
}
}
5.3.3 Lỗi import/named
Lỗi này xảy ra khi bạn cố gắng import tên không tồn tại trong module. Bạn cần đảm bảo đang import đúng tên đã được export trong module.
// Module
export const myFunction = () => {};
// Import đúng
import { myFunction } from './myModule';
5.3.4 Lỗi import/order
Lỗi này xảy ra khi bạn import không đúng thứ tự quy định.
Khắc phục bằng việc điều chỉnh lại import theo thứ tự:
- Import thư viện từ
node_modules
trước. - Import các module được định nghĩa trong dự án của bạn sau.
- Đây là cấu hình trong ESLint
{
"rules": {
"import/order": [
"error",
{
"groups": ["builtin", "external", "internal"],
"newlines-between": "always"
}
]
}
}
5.3.5 Lỗi import/prefer-default-export
Xảy ra khi module chỉ export
một thành phần duy nhất nhưng không sử dụng export default
- Hãy sử dụng
export default
nếu module chỉ export thành phần duy nhất
// Module
export default function myFunction() {}
// Import
import myFunction from './myModule';
- Hoặc bạn muốn sử dụng
named export
thì có thể off rule này đi
{
"rules": {
"import/prefer-default-export": "off"
}
}
6. Kết luận
ESLint là một công cụ phân tích mã tĩnh giúp kiểm tra và phát hiện các lỗi trong JavaScript trước khi thực thi. Sử dụng hiệu quả ESLint sẽ giúp bạn tăng cường chất lượng code, code clean hơn, giảm thiểu lỗi, đảm bảo sự nhất quán trong dự án.
Những chủ đề hay tại 200Lab có thể bạn quan tâm:
Bài viết liên quan
Cách Discord Lưu Trữ Hàng Nghìn Tỷ Tin Nhắn Với ScyllaDB
Dec 06, 2024 • 9 min read
Idempotent Consumer: Xử lý thông điệp trùng lặp trong Microservices
Dec 04, 2024 • 7 min read
Hướng dẫn tích hợp Redux và React Query trong dự án React Vite
Nov 22, 2024 • 8 min read
Giới thiệu Kiến trúc Backend for Frontend (BFF)
Nov 16, 2024 • 10 min read
Flask là gì? Hướng dẫn tạo Ứng dụng Web với Flask
Nov 15, 2024 • 7 min read
Webhook là gì? So sánh Webhook và API
Nov 15, 2024 • 8 min read