Typescript类型检查原理之Override怎么实现

这篇文章将为大家详细讲解有关Typescript 类型检查原理之Override怎么实现,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

我们提供的服务有:成都网站建设、网站设计、微信公众号开发、网站优化、网站认证、黄山区ssl等。为上千多家企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的黄山区网站制作公司

override 修饰符是干嘛的

首先,我们来看下这个修饰符的作用:被 override 标示的方法必须得在父类中存在,否则会报错。

class Animal {   getName() { return ''; } } class Dog extends Animal {   override bak() {     return 'wang';   }   override getName() {     return 'wang';   } }

上面这段代码会报错:This member cannot have an 'override' modifier because it is not  declared in the base class  'Animal'.就是说重写的放在父类不存在,这样能避免父类重构的时候把一些子类需要重写的方法给去掉。

Typescript 类型检查原理之Override怎么实现

如何实现 override 修饰符的类型检查

其实所有的修饰符,包括 override、public、static 等,在 parse 成 AST 后都是作为一个属性存在的,这个 override  也是,我们通过 astexplorer.net 来查看一下。

Typescript 类型检查原理之Override怎么实现

可以看到 override 属性为 true。这样我们就可以通过这个属性把该 class 的所有的需要 override 的 ClassMethod  过滤出来。

然后还可以拿到 superClass 的名字,从作用域中找到对应的声明,然后遍历 AST 找到它所声明的所有 ClassMethod。

两者对比一下,所有不在父类中的 ClassMethod 都需要报错。

代码实现

我们基于 babel 来做 parser 和分析,写一个插件来做 override 的类型检查。关于 babel 插件的基础可以看小册《babel  插件通关秘籍》。

开启语法 typescript 插件来解析 ts 语法。

const { transformFromAstSync } = require('@babel/core'); const  parser = require('@babel/parser');  const ast = parser.parse(sourceCode, {     sourceType: 'unambiguous',     plugins: ['typescript'] });  const { code } = transformFromAstSync(ast, sourceCode, {     plugins: [overrideCheckerPlugin] });

插件要处理的是 ClassDeclaration,我们先搭一个基本的结构:

const { declare } = require('@babel/helper-plugin-utils');  const overrideCheckerPlugin = declare((api, options, dirname) => {     api.assertVersion(7);      return {         pre(file) {             file.set('errors', []);         },         visitor: {             ClassDeclaration(path, state) {                 const semanticErrors = state.file.get('errors');                 //...                 state.file.set('errors', semanticErrors);             }         },         post(file) {             console.log(file.get('errors'));         }     } });

具体的检查逻辑是拿到父类的所有方法名,拿到当前类的所有 override 方法名,然后做下过滤。

我们首先要拿到父类的 ast,通过名字从作用域中查找。

const superClass = path.node.superClass; if (superClass) {     const superClassPath = path.scope.getBinding(superClass.name).path; }

然后封装一个方法来拿父类方法名,通过 path.traverse 来遍历 ast,把收集到的方法名存到 state 中。

function getAllClassMethodNames(classDeclarationNodePath) {     const state = {         allSuperMethodNames: []     }     classDeclarationNodePath.traverse({         ClassMethod(path) {             state.allSuperMethodNames.push(path.get('key').toString())         }     });     return state.allSuperMethodNames; }

这样就拿到了所有父类方法名。

之后需要拿到当前类的所有方法名并过滤出 override 为 true 且不在父类中的进行报错。

const superClass = path.node.superClass; if (superClass) {     const superClassPath = path.scope.getBinding(superClass.name).path;     const allMethodNames = getAllClassMethodNames(superClassPath);      path.traverse({         ClassMethod(path) {             if (path.node.override){                 const methodName = path.get('key').toString();                 const superClassName = superClassPath.get('id').toString();                 if (!allMethodNames.includes(methodName)) {                     // 报错                                                     }             }         }     }); }

报错的部分使用 code frame 来创建友好的代码打印格式,通过 Error.stackTraceLimit 设置为 0 去掉调用栈信息。

const tmp = Error.stackTraceLimit; Error.stackTraceLimit = 0; let errorMessage = `this member cannot have an 'override' modifier because it is not declared in the base class '${superClassName}'`; semanticErrors.push(path.get('key').buildCodeFrameError(errorMessage, Error)); Error.stackTraceLimit = tmp;

这样,我们就完成了 override 的类型检查,整体代码如下:

const { declare } = require('@babel/helper-plugin-utils');  function getAllClassMethodNames(classDeclarationNodePath) {     const state = {         allSuperMethodNames: []     }     classDeclarationNodePath.traverse({         ClassMethod(path) {             state.allSuperMethodNames.push(path.get('key').toString())         }     });     return state.allSuperMethodNames; }  const overrideCheckerPlugin = declare((api, options, dirname) => {     api.assertVersion(7);      return {         pre(file) {             file.set('errors', []);         },         visitor: {             ClassDeclaration(path, state) {                 const semanticErrors = state.file.get('errors');                  const superClass = path.node.superClass;                 if (superClass) {                     const superClassPath = path.scope.getBinding(superClass.name).path;                     const allMethodNames = getAllClassMethodNames(superClassPath);                              path.traverse({                         ClassMethod(path) {                             if (path.node.override){                                 const methodName = path.get('key').toString();                                 const superClassName = superClassPath.get('id').toString();                                 if (!allMethodNames.includes(methodName)) {                                     const tmp = Error.stackTraceLimit;                                     Error.stackTraceLimit = 0;                                     let errorMessage = `this member cannot have an 'override' modifier because it is not declared in the base class '${superClassName}'`;                                     semanticErrors.push(path.get('key').buildCodeFrameError(errorMessage, Error));                                     Error.stackTraceLimit = tmp;                                                                     }                             }                         }                     });                 }                 state.file.set('errors', semanticErrors);             }         },         post(file) {             console.log(file.get('errors'));         }     } });  module.exports = overrideCheckerPlugin;

github 链接

测试效果

我们用最开始的代码来测试一下:

class Animal {     getName() { return ''; } } class Dog extends Animal {     override bak() {         return 'wang';     }     override getName() {         return 'wang';     } }

打印信息为:

Typescript 类型检查原理之Override怎么实现

正确的识别出了 bak 在父类不存在的错误。

至此,我们实现了 override 的类型检查!

总结

类型检查情况很多,所以需要一个系列文章去讲,这一篇我们来实现 override 的类型检查。

override 是 ts 4.3 加入的特性,带有 override 修饰符的方法必须在父类中有对应的声明,否则会报错。

我们通过 babel 插件的方式实现了类型检查,思路是从作用域取出父类的声明,然后通过 path.traverse  拿到所有方法名,之后再取当前类的所有方法名,对于没在父类中声明并且带有 override 修饰符的方法进行报错。

关于“Typescript 类型检查原理之Override怎么实现”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。


当前题目:Typescript类型检查原理之Override怎么实现
文章链接:http://myzitong.com/article/gidijc.html