这篇文章将为大家详细讲解有关Typescript 类型检查原理之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、public、static 等,在 parse 成 AST 后都是作为一个属性存在的,这个 override 也是,我们通过 astexplorer.net 来查看一下。
可以看到 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'; } }
打印信息为:
正确的识别出了 bak 在父类不存在的错误。
至此,我们实现了 override 的类型检查!
类型检查情况很多,所以需要一个系列文章去讲,这一篇我们来实现 override 的类型检查。
override 是 ts 4.3 加入的特性,带有 override 修饰符的方法必须在父类中有对应的声明,否则会报错。
我们通过 babel 插件的方式实现了类型检查,思路是从作用域取出父类的声明,然后通过 path.traverse 拿到所有方法名,之后再取当前类的所有方法名,对于没在父类中声明并且带有 override 修饰符的方法进行报错。
关于“Typescript 类型检查原理之Override怎么实现”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。