Web前端之路

eslint手动检测typescript参数类型

2020-03-22  本文已影响0人  FingerStyle

最近在做eslint中的typescript参数类型检测,本来想直接用typescript-eslint-parser 检测,但发现项目中有些代码是用mobx注入的,typescript-eslint-parser检测不出来,于是找了些eslint和typescript的资料来看,经过一番实践之后,找到了一些思路。下面简单写一下如何在eslint中手动检测typescript中的参数类型。

  1. 首先,需要安装@typescript-eslint/typescript-estree,建议新建一个文件夹后执行,注意下跟typescript的版本兼容性
yarn add -D @typescript-eslint/typescript-estree
  1. 编写如下脚本,命名为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这个字段,里面就包括了参数的类型信息。

  1. 在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,
              },
          });
          }
        }
      },
    };
  },
};
上一篇 下一篇

猜你喜欢

热点阅读