自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Typescript 類(lèi)型檢查原理之Override 是如何實(shí)現(xiàn)的

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

[[403862]]

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

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

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

override 修飾符是干嘛的

首先,我們來(lái)看下這個(gè)修飾符的作用:被 override 標(biāo)示的方法必須得在父類(lèi)中存在,否則會(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'.就是說(shuō)重寫(xiě)的放在父類(lèi)不存在,這樣能避免父類(lèi)重構(gòu)的時(shí)候把一些子類(lèi)需要重寫(xiě)的方法給去掉。

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

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

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

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

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

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

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

開(kāi)啟語(yǔ)法 typescript 插件來(lái)解析 ts 語(yǔ)法。

  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. }); 

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

我們首先要拿到父類(lèi)的 ast,通過(guò)名字從作用域中查找。

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

然后封裝一個(gè)方法來(lái)拿父類(lèi)方法名,通過(guò) path.traverse 來(lái)遍歷 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; 

這樣就拿到了所有父類(lèi)方法名。

之后需要拿到當(dāng)前類(lèi)的所有方法名并過(guò)濾出 override 為 true 且不在父類(lèi)中的進(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 來(lái)創(chuàng)建友好的代碼打印格式,通過(guò) 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 的類(lèi)型檢查,整體代碼如下:

  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 鏈接

測(cè)試效果

我們用最開(kāi)始的代碼來(lái)測(cè)試一下:

  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 在父類(lèi)不存在的錯(cuò)誤。

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

總結(jié)

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

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

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

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

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

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

 

責(zé)任編輯:武曉燕 來(lái)源: 神光的編程秘籍
相關(guān)推薦

2021-06-09 07:55:19

Typescript類(lèi)型檢查

2022-04-11 08:42:09

TypeScript子類(lèi)型定義

2009-07-22 09:43:30

Scala類(lèi)型

2022-05-04 09:02:41

TypeScript類(lèi)型工具

2020-12-18 11:35:22

TypeScript語(yǔ)言Java

2022-04-10 19:26:07

TypeScript類(lèi)型語(yǔ)法

2024-07-30 10:27:10

TypeScript配置TS

2022-09-14 15:24:57

typescript快排

2012-07-02 10:43:49

JVMGroovyJava

2023-01-05 08:09:27

GroovyDSL?

2022-02-25 09:06:02

TypeScripnever工具

2021-07-27 06:06:34

TypeScript語(yǔ)言運(yùn)算符

2013-07-09 14:41:58

C動(dòng)態(tài)類(lèi)型

2023-06-13 18:24:26

TypeScriptJSDoc開(kāi)發(fā)

2022-02-09 08:11:50

架構(gòu)

2024-05-11 10:19:31

TypeScript類(lèi)型接口

2021-08-18 07:56:05

Typescript類(lèi)型本質(zhì)

2022-08-08 09:00:42

TypeScript映射類(lèi)型

2020-06-04 07:51:30

MySQL死鎖加鎖

2011-08-15 10:16:55

內(nèi)存泄露
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)