eslint手动检测typescript参数类型
最近在做eslint中的typescript参数类型检测,本来想直接用typescript-eslint-parser 检测,但发现项目中有些代码是用mobx注入的,typescript-eslint-parser检测不出来,于是找了些eslint和typescript的资料来看,经过一番实践之后,找到了一些思路。下面简单写一下如何在eslint中手动检测typescript中的参数类型。
首先,需要安装@typescript-eslint/typescript-estree,建议新建一个文件夹后执行,注意下跟typescript的版本兼容性
yarn add -D @typescript-eslint/typescript-estree
编写如下脚本,命名为ts_getnode.tsx,放在前面创建的文件夹下
###!/usr/bin/env ts-node
var fs = require("fs");
const parser = require('@typescript-eslint/typescript-estree');
console.log(process.argv)
const filepath = process.argv[2];//前面两个参数分别为ts-node的路径和test1.tsx的路径
const code = fs.readFileSync(filepath).toString();
console.log("文件内容",code);
const ast = parser.parse(code, {
range: true,
loc: true,
jsx:true//开启后检测jsx
});
// console.log(JSON.stringify(ast));
// console.log(ast);
fs.writeFile("output.txt",JSON.stringify(ast),(err:any)=>{
if (err) {
return console.error(err);
}
console.log("数据写入成功!");
})
3.执行上一步创建的脚本,参数为需要检测的文件路径,如/Users/luojin/Desktop/test_ts/test1.tsx
ts-node get_tsnode.tsx /Users/luojin/Desktop/test_ts/test1.tsx
这里,我是用了ts-node来在终端执行,你也可以放在其他任何能执行typescript脚本的环境中执行
4.跑出来的结果(AST)会写在同一个目录下的output.txt文件中,复制出来,粘贴到可以解析json的网站,如https://www.json.cn/就可以看到解析的结果。
以笔者自己写的一段代码测试代码为例:
function helloWorld (str:any) {
console.log(str)
}
helloWorld('hello world!')
解析出来是
{
"type":"Program",
"body":[
{
"type":"FunctionDeclaration",
"id":{
"type":"Identifier",
"name":"helloWorld",
"range":[
9,
19
],
"loc":{
"start":{
"line":1,
"column":9
},
"end":{
"line":1,
"column":19
}
}
},
"generator":false,
"expression":false,
"async":false,
"params":[
{
"type":"Identifier",
"name":"str",
"range":[
21,
28
],
"loc":{
"start":{
"line":1,
"column":21
},
"end":{
"line":1,
"column":28
}
},
"typeAnnotation":{
"type":"TSTypeAnnotation",
"loc":{
"start":{
"line":1,
"column":24
},
"end":{
"line":1,
"column":28
}
},
"range":[
24,
28
],
"typeAnnotation":{
"type":"TSAnyKeyword",
"range":[
25,
28
],
"loc":{
"start":{
"line":1,
"column":25
},
"end":{
"line":1,
"column":28
}
}
}
}
}
],
"body":{
"type":"BlockStatement",
"body":[
{
"type":"ExpressionStatement",
"expression":{
"type":"CallExpression",
"callee":{
"type":"MemberExpression",
"object":{
"type":"Identifier",
"name":"console",
"range":[
34,
41
],
"loc":{
"start":{
"line":2,
"column":2
},
"end":{
"line":2,
"column":9
}
}
},
"property":{
"type":"Identifier",
"name":"log",
"range":[
42,
45
],
"loc":{
"start":{
"line":2,
"column":10
},
"end":{
"line":2,
"column":13
}
}
},
"computed":false,
"optional":false,
"range":[
34,
45
],
"loc":{
"start":{
"line":2,
"column":2
},
"end":{
"line":2,
"column":13
}
}
},
"arguments":[
{
"type":"Identifier",
"name":"str",
"range":[
46,
49
],
"loc":{
"start":{
"line":2,
"column":14
},
"end":{
"line":2,
"column":17
}
}
}
],
"optional":false,
"range":[
34,
50
],
"loc":{
"start":{
"line":2,
"column":2
},
"end":{
"line":2,
"column":18
}
}
},
"range":[
34,
50
],
"loc":{
"start":{
"line":2,
"column":2
},
"end":{
"line":2,
"column":18
}
}
}
],
"range":[
30,
52
],
"loc":{
"start":{
"line":1,
"column":30
},
"end":{
"line":3,
"column":1
}
}
},
"range":[
0,
52
],
"loc":{
"start":{
"line":1,
"column":0
},
"end":{
"line":3,
"column":1
}
}
},
{
"type":"ExpressionStatement",
"expression":{
"type":"CallExpression",
"callee":{
"type":"Identifier",
"name":"helloWorld",
"range":[
53,
63
],
"loc":{
"start":{
"line":4,
"column":0
},
"end":{
"line":4,
"column":10
}
}
},
"arguments":[
{
"type":"Literal",
"raw":"'hello world!'",
"value":"hello world!",
"range":[
64,
78
],
"loc":{
"start":{
"line":4,
"column":11
},
"end":{
"line":4,
"column":25
}
}
}
],
"optional":false,
"range":[
53,
79
],
"loc":{
"start":{
"line":4,
"column":0
},
"end":{
"line":4,
"column":26
}
}
},
"range":[
53,
79
],
"loc":{
"start":{
"line":4,
"column":0
},
"end":{
"line":4,
"column":26
}
}
}
],
"sourceType":"script",
"range":[
0,
79
],
"loc":{
"start":{
"line":1,
"column":0
},
"end":{
"line":4,
"column":26
}
}
}
对比用tsc转成javascript后再用esprima解析(只能解析javascript)的结果. esprima网站: https://esprima.org/demo/parse.html#
{
"type": "Program",
"body": [
{
"type": "FunctionDeclaration",
"id": {
"type": "Identifier",
"name": "helloWorld"
},
"params": [
{
"type": "Identifier",
"name": "str"
}
],
"body": {
"type": "BlockStatement",
"body": [
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"computed": false,
"object": {
"type": "Identifier",
"name": "console"
},
"property": {
"type": "Identifier",
"name": "log"
}
},
"arguments": [
{
"type": "Identifier",
"name": "str"
}
]
}
}
]
},
"generator": false,
"expression": false,
"async": false
},
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "Identifier",
"name": "helloWorld"
},
"arguments": [
{
"type": "Literal",
"value": "hello world!",
"raw": "'hello world!'"
}
]
}
}
],
"sourceType": "script"
}
忽略loc和range,可以看到typescript主要是多了typeAnnotation这个字段,里面就包括了参数的类型信息。
在eslint的自定义规则里来拿到这个typeAnnotation进行处理,这个就不详细讲了,可以参考https://www.jianshu.com/p/1c805c52c51d
当前访问节点的所有信息都是从node 中拿到,只是需要注意Identifier才有typeAnnotation
笔者写了一个脚本,检查参数是否有any类型的,如下
module.exports = {
meta: {
docs: {
description: 'disallow any type',
category: 'Possible Errors',
recommended: true,
},
fixable: 'code',
schema: [], // no options
},
create: function(context) {
return {
Identifier: function(node) {
const { typeAnnotation,type } = node;
if (typeAnnotation != undefined) {
if (typeAnnotation.typeAnnotation.type == "TSAnyKeyword") {
context.report({
node,
message: '{{ str }} is any type',
data: {
str: node.name,
},
});
}
}
},
};
},
};
发表评论 (审核通过后显示评论):