日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Typescript類型檢查原理之Override是如何實(shí)現(xiàn)的

本文轉(zhuǎn)載自微信公眾號(hào)「神光的編程秘籍」,作者神說要有光 。轉(zhuǎn)載本文請聯(lián)系神光的編程秘籍公眾號(hào)。

作為一家“創(chuàng)意+整合+營銷”的成都網(wǎng)站建設(shè)機(jī)構(gòu),我們在業(yè)內(nèi)良好的客戶口碑。成都創(chuàng)新互聯(lián)公司提供從前期的網(wǎng)站品牌分析策劃、網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、成都做網(wǎng)站、創(chuàng)意表現(xiàn)、網(wǎng)頁制作、系統(tǒng)開發(fā)以及后續(xù)網(wǎng)站營銷運(yùn)營等一系列服務(wù),幫助企業(yè)打造創(chuàng)新的互聯(lián)網(wǎng)品牌經(jīng)營模式與有效的網(wǎng)絡(luò)營銷方法,創(chuàng)造更大的價(jià)值。

前段時(shí)間寫過一篇類型檢查的實(shí)現(xiàn)原理的文章,實(shí)現(xiàn)了簡單的賦值語句和函數(shù)調(diào)用的類型檢查。實(shí)際上類型檢查的情況特別多,一篇文章肯定寫不完,所以我準(zhǔn)備用系列文章來講述各種類型檢查的實(shí)現(xiàn)原理,幫助大家更好的掌握 typescript。

這一篇我們來實(shí)現(xiàn) 4.3 新增的 class 的 override 關(guān)鍵字的類型檢查。(源碼鏈接在后面)

override 修飾符是干嘛的

首先,我們來看下這個(gè)修飾符的作用:被 override 標(biāo)示的方法必須得在父類中存在,否則會(huì)報(bào)錯(cuò)。

 
 
 
 
  1. class Animal { 
  2.   getName() { return ''; } 
  3. class Dog extends Animal { 
  4.   override bak() { 
  5.     return 'wang'; 
  6.   } 
  7.   override getName() { 
  8.     return 'wang'; 
  9.   } 

上面這段代碼會(huì)報(bào)錯(cuò):This member cannot have an 'override' modifier because it is not declared in the base class 'Animal'.就是說重寫的放在父類不存在,這樣能避免父類重構(gòu)的時(shí)候把一些子類需要重寫的方法給去掉。

如何實(shí)現(xiàn) override 修飾符的類型檢查

其實(shí)所有的修飾符,包括 override、public、static 等,在 parse 成 AST 后都是作為一個(gè)屬性存在的,這個(gè) override 也是,我們通過 astexplorer.net 來查看一下。

可以看到 override 屬性為 true。這樣我們就可以通過這個(gè)屬性把該 class 的所有的需要 override 的 ClassMethod 過濾出來。

然后還可以拿到 superClass 的名字,從作用域中找到對應(yīng)的聲明,然后遍歷 AST 找到它所聲明的所有 ClassMethod。

兩者對比一下,所有不在父類中的 ClassMethod 都需要報(bào)錯(cuò)。

代碼實(shí)現(xiàn)

我們基于 babel 來做 parser 和分析,寫一個(gè)插件來做 override 的類型檢查。關(guān)于 babel 插件的基礎(chǔ)可以看小冊《babel 插件通關(guān)秘籍》。

開啟語法 typescript 插件來解析 ts 語法。

 
 
 
 
  1. const { transformFromAstSync } = require('@babel/core'); 
  2. const  parser = require('@babel/parser'); 
  3.  
  4. const ast = parser.parse(sourceCode, { 
  5.     sourceType: 'unambiguous', 
  6.     plugins: ['typescript'] 
  7. }); 
  8.  
  9. const { code } = transformFromAstSync(ast, sourceCode, { 
  10.     plugins: [overrideCheckerPlugin] 
  11. }); 

插件要處理的是 ClassDeclaration,我們先搭一個(gè)基本的結(jié)構(gòu):

 
 
 
 
  1. const { declare } = require('@babel/helper-plugin-utils'); 
  2.  
  3. const overrideCheckerPlugin = declare((api, options, dirname) => { 
  4.     api.assertVersion(7); 
  5.  
  6.     return { 
  7.         pre(file) { 
  8.             file.set('errors', []); 
  9.         }, 
  10.         visitor: { 
  11.             ClassDeclaration(path, state) { 
  12.                 const semanticErrors = state.file.get('errors'); 
  13.                 //... 
  14.                 state.file.set('errors', semanticErrors); 
  15.             } 
  16.         }, 
  17.         post(file) { 
  18.             console.log(file.get('errors')); 
  19.         } 
  20.     } 
  21. }); 

具體的檢查邏輯是拿到父類的所有方法名,拿到當(dāng)前類的所有 override 方法名,然后做下過濾。

我們首先要拿到父類的 ast,通過名字從作用域中查找。

 
 
 
 
  1. const superClass = path.node.superClass; 
  2. if (superClass) { 
  3.     const superClassPath = path.scope.getBinding(superClass.name).path; 

然后封裝一個(gè)方法來拿父類方法名,通過 path.traverse 來遍歷 ast,把收集到的方法名存到 state 中。

 
 
 
 
  1. function getAllClassMethodNames(classDeclarationNodePath) { 
  2.     const state = { 
  3.         allSuperMethodNames: [] 
  4.     } 
  5.     classDeclarationNodePath.traverse({ 
  6.         ClassMethod(path) { 
  7.             state.allSuperMethodNames.push(path.get('key').toString()) 
  8.         } 
  9.     }); 
  10.     return state.allSuperMethodNames; 

這樣就拿到了所有父類方法名。

之后需要拿到當(dāng)前類的所有方法名并過濾出 override 為 true 且不在父類中的進(jìn)行報(bào)錯(cuò)。

 
 
 
 
  1. const superClass = path.node.superClass; 
  2. if (superClass) { 
  3.     const superClassPath = path.scope.getBinding(superClass.name).path; 
  4.     const allMethodNames = getAllClassMethodNames(superClassPath); 
  5.  
  6.     path.traverse({ 
  7.         ClassMethod(path) { 
  8.             if (path.node.override){ 
  9.                 const methodName = path.get('key').toString(); 
  10.                 const superClassName = superClassPath.get('id').toString(); 
  11.                 if (!allMethodNames.includes(methodName)) { 
  12.                     // 報(bào)錯(cuò)                                     
  13.                 } 
  14.             } 
  15.         } 
  16.     }); 

報(bào)錯(cuò)的部分使用 code frame 來創(chuàng)建友好的代碼打印格式,通過 Error.stackTraceLimit 設(shè)置為 0 去掉調(diào)用棧信息。

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

這樣,我們就完成了 override 的類型檢查,整體代碼如下:

 
 
 
 
  1. const { declare } = require('@babel/helper-plugin-utils'); 
  2.  
  3. function getAllClassMethodNames(classDeclarationNodePath) { 
  4.     const state = { 
  5.         allSuperMethodNames: [] 
  6.     } 
  7.     classDeclarationNodePath.traverse({ 
  8.         ClassMethod(path) { 
  9.             state.allSuperMethodNames.push(path.get('key').toString()) 
  10.         } 
  11.     }); 
  12.     return state.allSuperMethodNames; 
  13.  
  14. const overrideCheckerPlugin = declare((api, options, dirname) => { 
  15.     api.assertVersion(7); 
  16.  
  17.     return { 
  18.         pre(file) { 
  19.             file.set('errors', []); 
  20.         }, 
  21.         visitor: { 
  22.             ClassDeclaration(path, state) { 
  23.                 const semanticErrors = state.file.get('errors'); 
  24.  
  25.                 const superClass = path.node.superClass; 
  26.                 if (superClass) { 
  27.                     const superClassPath = path.scope.getBinding(superClass.name).path; 
  28.                     const allMethodNames = getAllClassMethodNames(superClassPath); 
  29.          
  30.                     path.traverse({ 
  31.                         ClassMethod(path) { 
  32.                             if (path.node.override){ 
  33.                                 const methodName = path.get('key').toString(); 
  34.                                 const superClassName = superClassPath.get('id').toString(); 
  35.                                 if (!allMethodNames.includes(methodName)) { 
  36.                                     const tmp = Error.stackTraceLimit; 
  37.                                     Error.stackTraceLimit = 0; 
  38.                                     let errorMessage = `this member cannot have an 'override' modifier because it is not declared in the base class '${superClassName}'`; 
  39.                                     semanticErrors.push(path.get('key').buildCodeFrameError(errorMessage, Error)); 
  40.                                     Error.stackTraceLimit = tmp;                                     
  41.                                 } 
  42.                             } 
  43.                         } 
  44.                     }); 
  45.                 } 
  46.                 state.file.set('errors', semanticErrors); 
  47.             } 
  48.         }, 
  49.         post(file) { 
  50.             console.log(file.get('errors')); 
  51.         } 
  52.     } 
  53. }); 
  54.  
  55. module.exports = overrideCheckerPlugin; 

github 鏈接

測試效果

我們用最開始的代碼來測試一下:

 
 
 
 
  1. class Animal { 
  2.     getName() { return ''; } 
  3. class Dog extends Animal { 
  4.     override bak() { 
  5.         return 'wang'; 
  6.     } 
  7.     override getName() { 
  8.         return 'wang'; 
  9.     } 

打印信息為:

正確的識(shí)別出了 bak 在父類不存在的錯(cuò)誤。

至此,我們實(shí)現(xiàn)了 override 的類型檢查!

總結(jié)

類型檢查情況很多,所以需要一個(gè)系列文章去講,這一篇我們來實(shí)現(xiàn) override 的類型檢查。

override 是 ts 4.3 加入的特性,帶有 override 修飾符的方法必須在父類中有對應(yīng)的聲明,否則會(huì)報(bào)錯(cuò)。

我們通過 babel 插件的方式實(shí)現(xiàn)了類型檢查,思路是從作用域取出父類的聲明,然后通過 path.traverse 拿到所有方法名,之后再取當(dāng)前類的所有方法名,對于沒在父類中聲明并且?guī)в?override 修飾符的方法進(jìn)行報(bào)錯(cuò)。

本文是 【typescript 類型檢查原理】系列文章的第二篇,后續(xù)還會(huì)有更多 typescript 類型檢查的實(shí)現(xiàn)原理揭秘的文章。希望能夠幫助大家更好的掌握 typescript。

關(guān)于 babel 插件的知識(shí),可以看我的 babel 小冊《babel 插件通關(guān)秘籍》,其中有詳細(xì)的講解。

本文源碼鏈接 https://github.com/QuarkGluonPlasma/babel-plugin-exercize/tree/master/exercize-type-checker/src


本文標(biāo)題:Typescript類型檢查原理之Override是如何實(shí)現(xiàn)的
本文網(wǎng)址:http://www.5511xx.com/article/dhodogj.html