这篇文章主要介绍了Typescript类型检查原理之Override如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Typescript类型检查原理之Override如何实现文章都会有所收获,下面我们一起来看看吧。
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'.就是说重写的放在父类不存在,这样能避免父类重构的时候把一些子类需要重写的方法给去掉。
如何实现 override 修饰符的类型检查
其实所有的修饰符,包括 override、public、static 等,在 parse 成 AST 后都是作为一个属性存在的,这个 override 也是,我们通过 astexplorer.net 来查看一下。
可以看到 override 属性为 true。这样我们就可以通过这个属性把该 class 的所有的需要 override 的 ClassMethod 过滤出来。
然后还可以拿到 superClass 的名字,从作用域中找到对应的声明,然后遍历 AST 找到它所声明的所有 ClassMethod。
两者对比一下,所有不在父类中的 ClassMethod 都需要报错。
代码实现
我们基于 babel 来做 parser 和分析,写一个插件来做 override 的类型检查。
开启语法 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;
测试效果
我们用最开始的代码来测试一下:
class Animal { getName() { return ''; } } class Dog extends Animal { override bak() { return 'wang'; } override getName() { return 'wang'; } }
打印信息为:
正确的识别出了 bak 在父类不存在的错误。
关于“Typescript类型检查原理之Override如何实现”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Typescript类型检查原理之Override如何实现”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网行业资讯频道。