Tiếp nối phần 1 - ✅ Check-list và 📝 note khi tạo base source-code cho Angular, phần này mình sẽ tập chung vào eslint
.
Chú ý trong bài viết mình sẽ sử dụng alias của 3 mức độ của rule trong eslint. Ví dụ dùng 2
chứ không dùng "error"
:
- "off" or 0 - turn the rule off
- "warn" or 1 - turn the rule on as a warning (doesn't affect exit code)
- "error" or 2 - turn the rule on as an error (exit code will be 1)
angular-eslint
Đối với phiên bản v12 trở đi, hãy để ng
làm hết cho ta:
ng add @angular-eslint/schematics
Và thế là xong, ta đã có .eslintrc.json
xịn bao gồm cả các plugin:
// "*.ts"
{
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
// "*.html"
"extends": [
"plugin:@angular-eslint/template/recommended"
],
}
Từ giờ ta có thể lint toàn project bằng:
pnpm lint
# or
yarn lint
# or
npm run lint
Ngoài ra còn có thể dùng thêm eslint-plugin-rxjs.
Linting HTML files và inline-templates với VSCode extension
Trước hết inline-templates
là kiểu viết template bên trong .ts
code.
Để eslint extention chạy tốt thì ta thêm setting vào settings.json
:
// ... more config
"eslint.options": {
"extensions": [".ts", ".html"]
},
// ... more config
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"html"
],
// ... more config
Các rule nên thêm
{
"overrides": [
{
"files": ["*.ts"],
// ... more config
"rules": {
"no-console": 1,
"no-alert": 2,
"no-useless-concat": 2,
"prefer-template": 2,
"template-curly-spacing": 2
// ... more config
}
// ... more config
},
}
"no-console": 1
là warning khi dùngconsole
"no-alert": 2
là báo lôi khi dùngalert
"no-useless-concat": 2
báo lỗi khi dùng toán tử+
để làm những việc vô nghĩa, ví dụ:
// đừng :(
const str = "Hello, " + "World!"
// viết luôn thế này đi :)
const greeting = "Hello, World!"
"prefer-template": 2
không cho dùng:
// đừng
var str = "Hello, " + name + "!";
var str = "Time: " + (12 * 60 * 60 * 1000);
// dùng thế này đi (kiểu template)
var str = `Hello, ${name}!`;
var str = `Time: ${12 * 60 * 60 * 1000}`;
Cách viết kiểu template
đúng chuẩn ES đời mới mà dễ đọc và nhanh hơn chuẩn cũ. Phép template
cũng chỉ dùng với String, type cũng chặt hơn.
Hạn chế code dài, khó bảo trì, khó hiểu
"max-lines": [
2,
400
]
Giới hạn số lượng dòng code tối đa là 400 dòng (ví dụ trên). Nếu lớn hơn thì cần tách code ra file khác.
"max-len": [
2,
{
"code": 120
}
]
Giới hạn số lượng ký tự 1 dòng code, tối đa là 120 ký tự 1 dòng chẳng hạn. Dự án cá nhân thì mình để 80 ký tự thôi để code chia đôi màn hình cho dễ nhìn. Nếu lớn hơn thì cần tách code xuống dòng. Khi lượng code 1 dòng không quá lớn thì tiện đọc code hơn rất nhiều, bạn cứ thử mà xem!
Thường thì cả team đều sử dụng prettier và không có thực tập sinh thì có thể bỏ qua rule này vì prettier đã xuống dòng cho mình rồi. Trong trường hợp đó, xóa rule này đi cho đỡ nặng eslint. Tuy nhiên, nhỡ có thanh niên nào không chịu bật prettier plugin ở editor lên thì phải bật rule này lên.
"max-lines-per-function": [2, 127]
Giới hạn số lượng dòng code tối đa 1 function là 127 dòng (ví dụ trên). Nếu lớn hơn thì cần tách code ra function khác.
"@typescript-eslint/no-empty-interface": 2
Không cho phép interface
rỗng.
"@angular-eslint/directive-selector": [
2,
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
]
Luật dành riêng cho Angular, directive-selector
- tên của directive
phải có tiền tố là app
để phân biệt với các directive
không được tạp ra bởi App, ví dụ directive
từ thư viện bên thứ 3.
"@angular-eslint/component-selector": [
2,
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
]
Tương tự với component
.
"no-else-return": 2
Ví dụ, đoạn code dưới đây khá là rườm rà:
function foo() {
if (x) {
return y;
} else {
return z;
}
}
Thực ra chỉ cần:
function foo() {
if (x) {
return y;
}
return z;
}
Nếu bạn muốn check xem có biến nào không được sử dụng từ bước coding trên editor mà chưa cần TypeScript build ra lỗi đó thì có thể dùng no-unused-vars từ typescript-eslint
.
"no-unused-vars": 0,
"@typescript-eslint/no-unused-vars": [2],
Ngoài ra, còn rất nhiều rule hay ho mà mình chưa thể nói hết ở bài này!
Cách đặt tên
Luật đặt tên là cần thiết nếu team hoặc khách hàng yêu cầu đặt tên theo chuẩn, ví dụ interface
phải dùng chữ I
(viết hoa) trước mỗi tên interface. Việc viết tên như thế khiến ta hiểu ngay đó là 1 interface chứ không nhầm với type
hoặc class chẳng hạn.
Ta sử dụng @typescript-eslint
:
"@typescript-eslint/naming-convention": [
2,
{
"selector": "interface",
"format": ["StrictPascalCase"],
"custom": {
"regex": "^I[A-Z]",
"match": true
}
},
{
"selector": "variable",
"format": ["strictCamelCase", "UPPER_CASE", "snake_case"]
},
{
"selector": "typeParameter",
"format": ["PascalCase"],
"prefix": ["T"]
},
{
"selector": "variable",
"types": ["boolean"],
"format": ["PascalCase"],
"prefix": ["is", "should", "has", "can", "did", "will"]
},
{
"selector": "enum",
"format": ["PascalCase"]
},
{
"selector": "enumMember",
"format": ["PascalCase"]
}
]
Dùng eslint để bắt buộc viết doc/comment cho method/function
Nếu khách hàng yêu cầu viết doc cho toàn bộ method/function thì mình nên dùng cái này. Ngược lại nếu không yêu cầu thì chỉ nên viết khi cần thiết, tiện và linh hoạt hơn, nên bạn có thể bỏ qua phần này.
Bạn có thể dùng eslint-plugin-require-jsdoc-except.
Sau khi cài đặt như hướng dẫn bên trên thì ta có thể config:
{
"plugins": [
// other plugin
"require-jsdoc-except"
]
// other config
"require-jsdoc-except/require-jsdoc": [2, {
"require": {
"FunctionDeclaration": true,
"MethodDefinition": true,
"ClassDeclaration": false,
"ArrowFunctionExpression": false,
"FunctionExpression": false
},
"ignore": [
"constructor",
"ngOnChanges",
"ngOnInit",
"ngDoCheck",
"ngAfterContentInit",
"ngAfterContentChecked",
"ngAfterViewInit",
"ngAfterViewChecked",
"ngOnDestroy"
]
}],
"valid-jsdoc": [2, {
"requireReturn": false,
"requireReturnType": false,
"requireParamType": false
}],
}
Trong đó:
ClassDeclaration
quyết định xem có cần viết comment hay không. Tương tự vớiMethodDefinition
,FunctionDeclaration
...ignore
sẽ loại bỏ tên những method không cần viết jsdoc.
Photo by Daniele Colucci on Unsplash