ESLint 为团队和项目带来的益处,这里不多说,本文主要是个人近期修改项目代码一些错误修改整理的内容。

备注:尽量使用默认 Prettier 规则,根据实际情况略有调整。项目技术栈 React, Redux, Redux-Sagas, TypeScript。

Plugin 列表

  • "plugin:react/recommended"
  • "plugin:@typescript-eslint/recommended"
  • "prettier/@typescript-eslint"
  • "plugin:prettier/recommended"

ERROR List

  • [Error] eslint@typescript-eslint/ban-ts-comment

    Do not use “// @ts-nocheck” because it alters compilation errors. Desc link

    TypeScript 提供 @ts-expect-error @ts-ignore @ts-nocheck @ts-check 指令注释方式,用来改变 tsc 编译时处理文件的方式,如果大量使用此类注释影响 TypeScript 的特性,既然使用 ts 就要拥抱它的特性,lint 中默认对 no-check 会按照 error 进行提示;可以根据实际情况调整规则,改为 warn

  • [Error] @typescript-eslint/ban-types

    Desc Link

    补充提示:

1
2
3
4
Don't use `{}` as a type. `{}` actually means "any non-nullish value".

- If you want a type meaning "any object", you probably want `Record<string, unknown>` instead.
- If you want a type meaning "any value", you probably want `unknown` instead.
  • [Error] react/jsx-no-target-blank

    Desc Link

    出于安全考虑,React 中产生新打开页面的链接,需要增加 rel='noreferrer' 用来保护原站。具体说明详见:Link

WARN List

  • [Warn] Missing return type on function.eslint@typescript-eslint/explicit-module-boundary-types

    desc link
    说明:针对函数的定义,建议每一个函数都要显式的表明函数返回值。这在 *.jsx, *.tsx 文件中,React 生命周期函数都提示,可以使用 eslint overrides 规则,只针对 *.js, *.ts 生效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"rules": {
// disable the rule for all files
"@typescript-eslint/explicit-module-boundary-types": "off"
},
"overrides": [
{
// enable the rule specifically for TypeScript files
"files": ["*.js", "*.ts"],
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "warn"
}
}
]
}
  • [Warn] @typescript-eslint/no-unused-vars

应该是最经常遇到的一个警告,定义了变量,下文没有使用。

个人建议:非关键算法或逻辑代码,当你阅读时没用,就删掉吧,可以保留注释,因为即使你想保留这段代码,以备不时之需,但是真到需要用到代码你再次阅读的时候,实现思路以及上下文联系可能已经相差很远。

Prettier 细节

  • 关于默认逗号的变化 link

Prettier 自动 v2.0.0 开始,将 trailingComma 默认配置由 none 改为 es5,在我看来是一种很好的方式。

举个例子,当 import 多个内容、一个对象需要增加属性、一个数组追加元素,如果默认已经追加了逗号,那么就可以直接追加,而不必要移动光标去前一行手动增加一个逗号,这样就增加了一些便利性,与此同时便于快速定位甚至避免由于一个逗号的引起的错误。

TS2322 自定义属性

1
2
3
4
5
6
7
8
import { AriaAttributes, DOMAttributes } from "react";

declare module "react" {
interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
// extends React's HTMLAttributes
custom?: string;
}
}

参考:https://dev.to/lukethacoder/use-custom-html-attribute-s-in-typescript-2co

TS2679

问题实例片段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function* loadSimilar(skuId: string) {
yield put({
type: ActionTypes.REQUEST_SIMILAR.PENDING,
});
try {
const { data } = yield API.querySimilar(skuId);
yield put({
type: ActionTypes.REQUEST_SIMILAR.SUCCESS,
data,
});
} catch (error) {
yield put({
type: ActionTypes.REQUEST_SIMILAR.FAILURE,
});
}
}

function* watchSimilar() {
yield takeEvery(ActionTypes.LOAD_SIMILAR, loadSimilar);
}

修改后代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- function* loadSimilar(skuId: string) {
+ function* loadSimilar({ skuId }: { type: string; skuId: string }) {
yield put({
type: ActionTypes.REQUEST_SIMILAR.PENDING,
});
try {
const { data } = yield API.querySimilar(skuId);
yield put({
type: ActionTypes.REQUEST_SIMILAR.SUCCESS,
data,
});
} catch (error) {
yield put({
type: ActionTypes.REQUEST_SIMILAR.FAILURE,
});
}
}

function* watchSimilar() {
yield takeEvery(ActionTypes.LOAD_SIMILAR, loadSimilar);
}

解答:Redux-Saga 中 takeEvery 第二个参数是一个 action,所以定义 loadSimilar 时候需要遵循 TakeableChannel<unknown> ,定义 type。
参考:https://stackoverflow.com/a/60558041

切记,保证一路传递参数变量匹配,否则会出现无法赋值问题,例如下面代码:

1
2
3
4
5
6
7
8
- function* cartOptCheckOne({ param }: { type: string; param: any }) {
+ function* cartOptCheckOne({
+ // type = ActionTypes.OPT_CARTCHECKONE,
+ RequestParam,
+ }: {
+ type: string;
+ RequestParam: any;
+ }) {

外层触发 action 传参数代码如下:

1
2
3
this.props.optCartCheckOne({
RequestParam: requestParam,
});

如果定义 cartOptCheckOne 使用 param 就会导致传递过程中因为变量名称不同无法解构,导致传参中断。

ESLint Config

参考 https://www.robertcooper.me/using-eslint-and-prettier-in-a-typescript-project