TSLint中常见的问题及处理方法

本文中很多地方会用ts表示typescript。本文主要针对刚使用typescript和tslint的同学,新手使用TSLint规则校验typescript代码时,总会碰到一些TSLint的错误提示,花费较多的时间和精力去解决这些错误信息,本篇文章将列出本人在项目开发中使用TSLint碰到的问题,及解决方案。TSLint是可配置的,如果项目中引用了TSLint,会有一个tslint.json的配置文件,可以通过其中的配置项去选择需要校验的规则,不知道如何配置的同学可以访问:
[link]: https://palantir.github.io/tslint/rules/ 网上也有很多关于配置的文章。
声明:本文中TSLint的提示信息为本次项目中tslint.json所配置的校验规则。

声明、导入

当我们在使用React + js 时,声明一个变量、声明一个函数、导入一个模块等等,声明或导入后没有使用,也不会报错。程序也能正常运行。但在ts中声明了就需要去使用,不然就会。。。

举个例子,本文会重复使用这个例子作为参考

这里是第一个例子,本人用截图的方式把代码和错误提示展示出来,后面的错误提示或者运行时的错误信息,会在例子的下面标注出来,会在例子的下面标注出来,会在例子的下面标注出来。好了,重要的事情说三遍。

从上图中可以看出,错误提示很明显,IConfig声明了但没有引用。声明变量、声明函数、导入模块等,声明或导入后没有引用就会有上图这样的错误提示,或者运行时会有大概一下几种错误提示:

  1. ‘***’ is declared but its value is never read.
  2. ‘IConfig’ is declared but never used.
  3. All imports in import declaration are unused.

总之碰到这些类型错误的时候还是很好解决的,我们去使用它不就得了。还是上面的这个例子,作为第一个解决方案,我们也使用截图。。。

这时就没有错误提示啦,是不是很开心。。。故事当然不会这么快就结束。运行时出现了这个错误:An empty interface is equivalent to {}.因为我们在tslint.json中设置了”no-empty-interface”:true,我们需要这样做:

1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react';
interface IConfig {
orgName: 'bwa'
}
class Test extends React.Component<IConfig> {
public render() {
return (
<div />
);
}
}
export default Test;

声明的错误类型暂时到这里到这里,上面的代码已经可以正常运行。

tslint这样处理的优点

  1. 避免了不必要的变量声明,及减少了从未使用到的僵尸代码。
  2. 避免了不必要的导入模块、函数,使整个项目更加轻量级。
  3. 可读性更高。

导入一个接口或一个方法时tslint的提示

现在我们的编辑器都相当的智能,本人使用的是VSCode,把鼠标移到方法上面就会有他的使用方式。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router';
interface IConfig {
orgName: 'bwa';
}
class Test extends React.Component<IConfig & RouteComponentProps<>> {
public render() {
return (
<div />
);
}
}
export default Test;

上面的代码在使用RouteComponentProps这个路由接口时tslint会有:Generic type ‘RouteComponentProps<P, C>’ requires between 1 and 2 type arguments. RouteComponentProps需要1~2个参数。我们只需要改动:

1
class Test extends React.Component<IConfig | RouteComponentProps<{name: gtx}>> {}

{name: gtx}里的name表示路由上的’/files/data/:name’。

鼠标放在withRouter上时,提示他是一个函数,它继承了RouteComponentProps接口,它有一个参数,并且这个参数的类型是组件类型,这个方法返回的是一个组件,这个组件拥有了RouteComponentProps里面定义的参数。

用法:

1
export default withRouterTest(Test);

tslint这样处理的优点

  1. tslint较为直观的提示我们接口、方法应该怎样使用,需要的参数类型。
  2. 不需要编译就能知道错误的准确位置,更正方法。

    对象的键排序,这里使用的是按字母的顺序排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Test extends React.Component<IConfig & RouteComponentProps<{ name: 'gtx' }>> {
public state: {
age: number;
sex: string;
} = {
sex: '男',
age: 18,
};
public render() {
return (
<div />
);
}
}

这是我们编译时,浏览器会提示:The key ‘age’ is not sorted alphabetically 需要按字母顺序排序。关于键需要按字母顺序排序之类的问题,很好理解我们就不多说了。

1
2
3
4
{
age: 18,
sex: '男',
};

当两个对象的键比较多且要求相等,我们有时会落下其中的某个键,如果不是按一定规则排序的话就会非常难寻找。

tslint这样处理的优点

  1. 容易比对出是什么地方落下的某个键,(小技巧:按字母排序的话,比对同一个字母开头的键的开始行与结束行🙂);

类型问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router';
interface IConfig {
orgName: 'bwa';
}
interface IData {
data: IConfig[]
};
class Test extends React.Component<IConfig & RouteComponentProps<{ name: 'gtx' }>> {
public state: {
age: number;
sex: string;
results: IData;
} = {
age: 18,
sex: '男',
};
public render() {
return (
<div />
);
}
}
export default withRouter(Test);

这时我们的state会报:Type ‘{ sex: string; age: number; }’ is not assignable to type ‘{ age: number; sex: string; results: IData; }’.
Property ‘results’ is missing in type ‘{ sex: string; age: number; }’. 类型XXX不能赋给类型XXX ,仔细看里面的提示理会发现没有给state里面的键results赋值。好吧,根据提示我们来给他赋值

1
2
3
4
5
6
7
8
9
10
11
 public state: {
age: number;
results: IData;
sex: string;
} = {
age: 18,
results: {
data: []
},
sex: '男',
};

看到这里你可能会说如果键的类型有多种呐,并且tslint.json中设置了no-any,那我怎么声明键的类型呐?嗯,往下看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  public state: {
age: number;
hobby: number | string | string[] | IData[] | IData;
results: IData;
sex: string;

} = {
age: 18,
hobby: 1 || '男' || {data: 'data'},
results: {
data: []
},
sex: '男',
};

1 || ‘男’ || {data: ‘data’} 中任何一种都可以赋值给hobby,只写其中一个也是可以的。

tslint这样处理的优点

  1. 当我们指定了类型,它就只能是这种类型,这样的定义,强类型的变量、参数写出来的代码更加的严谨,后期的维护更加的方便。
  2. 可读性更高。

    声明变量与组件的名字一样或与引入组件的名字一样时

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     class Test extends React.Component {
    public child = () => {
    const Test = <hr />;
    return (
    Test
    );
    }
    public render() {
    return (
    <div>
    {this.child()}
    </div>
    );
    }
    }

错误提示: Shadowed name: ‘Test’,此时我们只需要重新声明变量的名字如const Test –>const hr;

tslint这样处理的优点

  1. 避免了作用域的混淆(声明变量与组件的名字一样或与引入组件的名字一样,调用时很容易出错,往往这样的错误让人很头疼)。
  2. 对开发者更加的友好(不用凭记忆去衡量模块命名是否已占用了这个变量名)。

console打印信息

在js中我们常用console打印一些信息在控制台,在tslin中很多时候会禁用掉console

tslint.json—> no-console: true; 这时你使用console.log的话就会:Calls to ‘console.log’ are not allowed.

如何解决呢?

1
2
import debug from 'debug';
const log = debug('Component:Test');

这时我们需要打印信息时调用log(‘想打印的内容’);同时在chrome浏览器 –>开发者模式 –> Application –> Local Storage 里添加: Key: debug, Value: Component:Test,这时我们就能打印出想打印的内容。

如图

这样做的优点:

  1. 定义了是哪个组件下打印的内容 :Component:Test 这里是Test下的
  2. Local Storage 里设置了需要接收的组件,这样的好处在于,我们在代码里写了log,但不需要去清除,其他电脑访问相同页面时控制台也会很干净,不会打印出这些信息。
  3. 不同组件下的log信息会以不同的颜色区分。

本篇文章暂时就到这里。


作者信息:宁文飞,人和未来大数据前端工程师