开发工具【免费下载链接】ts-morphTypeScript Compiler API wrapper for static analysis and programmatic code changes.项目地址https://gitcode.com/gh_mirrors/ts/ts-morph点击查看免费下载本文基于 ts-morph 官方文档 docs/details/modifiers.md并结合仓库源码深入讲解修饰符Modifier的完整 API如何获取、按语法类型查询、判断存在性以及切换private、async、export等修饰关键字。读完本文你将掌握在静态分析与程序化代码修改中正确读写修饰符的全部手段并理解其底层实现与边界行为。什么是修饰符在 TypeScript 中修饰符是用于“修饰”其他节点的关键字节点。例如private关键字会改变类方法的可见作用域async声明函数为异步export决定模块是否对外暴露符号。在 ts-morph 的 AST 中这些关键字被表示为Nodets.Modifier类型的节点位于目标声明节点的modifiers列表里。并不是所有节点都能拥有修饰符只有满足条件的节点才会混入ModifierableNode从而获得下面将要介绍的一整套修饰符操作函数。源码实现位于 ModifierableNode.ts其类型定义明确了所有受支持的修饰符文本ModifierTextsexport type ModifierTexts | export | default | declare | abstract | public | protected | private | readonly | static | async | const | override | in | out | accessor;这 15 种文本覆盖了 TS 中绝大多数修饰关键字也是hasModifier(text)、toggleModifier(text)等字符串重载所接受的值集合。哪些节点拥有修饰符从源码中可以看到ModifierableNode被大量声明类混入典型场景包括对应文件位于 ast/base 目录类成员方法 MethodDeclaration.ts、属性 PropertyDeclaration.ts、构造函数 ConstructorDeclaration.ts类声明通过 ClassLikeDeclarationBase.ts 混入函数与变量函数声明 FunctionDeclaration.ts重载签名部分、函数表达式 FunctionExpression.ts、变量声明语句 VariableStatement.ts模块与导入模块声明 ModuleDeclaration.ts、导入等值声明 ImportEqualsDeclaration.ts枚举、接口与类型枚举声明 EnumDeclaration.ts、接口声明 InterfaceDeclaration.ts、属性签名 PropertySignature.ts、索引签名 IndexSignatureDeclaration.ts、类型别名 TypeAliasDeclaration.ts、类型参数 TypeParameterDeclaration.ts、参数声明 ParameterDeclaration.ts 等。因此只要你在代码中操作上述任一节点都可以直接调用getModifiers()等函数无需先做类型断言。读取全部修饰符getModifiersgetModifiers()返回该节点全部修饰符组成的数组元素为包装后的Nodets.Modifier对象可以继续调用节点通用方法如getText()、getKind()。functionDeclaration.getModifiers();源码实现ModifierableNode.ts非常简单getModifiers() { return this.getCompilerModifiers().map(m this._getNodeFromCompilerNode(m)) as Nodets.Modifier[]; }它先读取编译器节点上的modifiers数组见私有方法getCompilerModifiers()读取(this.compilerNode as any).modifiers ?? []再把每个编译器节点包装成 ts-morph 的Node返回。测试用例验证了返回顺序与原始代码一致例如对于export abstract class Identifier {}getModifiers()依次返回ExportKeyword与AbstractKeyword两个节点见 modifierableNodeTests.ts。按语法类型获取第一个修饰符getFirstModifierByKind 与 OrThrow 变体当只需要判断某个特定关键字是否存在时使用getFirstModifierByKind(syntaxKind: SyntaxKind)functionDeclaration.getFirstModifierByKind(SyntaxKind.AsyncKeyword);例如要拿到函数声明中的async关键字节点const asyncKeyword functionDeclaration.getFirstModifierByKind(SyntaxKind.AsyncKeyword); if (asyncKeyword ! null) console.log(asyncKeyword.getText()); // async其实现ModifierableNode.ts会遍历编译器修饰符找到第一个kind匹配的节点并返回包装结果找不到则返回undefined而不是抛错。返回类型被精确映射为KindToNodeMappings[TKind]因此你能获得对应关键字节点的强类型如Nodets.AsyncKeyword。如果希望“找不到就报错”使用抛错版本const asyncKeyword functionDeclaration.getFirstModifierByKindOrThrow(SyntaxKind.AsyncKeyword);getFirstModifierByKindOrThrowModifierableNode.ts内部通过errors.throwIfNullOrUndefined实现找不到时抛出默认错误信息Expected a modifier of syntax kind: AsyncKeyword你也可以传入自定义 message。测试覆盖了“存在返回节点”与“不存在抛异常”两种路径modifierableNodeTests.ts。判断是否拥有修饰符hasModifierhasModifier是唯一一个同时支持“语法类型”与“文本”两种参数的查询 APIfunctionDeclaration.hasModifier(SyntaxKind.AsyncKeyword); // 按语法类型返回 boolean functionDeclaration.hasModifier(async); // 按修饰符文本返回 boolean实现ModifierableNode.ts对字符串参数采用m.getText() textOrKind精确比较对SyntaxKind参数则用m.kind textOrKind比较hasModifier(textOrKind: ModifierTexts | SyntaxKind) { if (typeof textOrKind string) return this.getModifiers().some(m m.getText() textOrKind); else return this.getCompilerModifiers().some(m m.kind textOrKind); }注意字符串比较是精确匹配的hasModifier(async)与hasModifier(SyntaxKind.AsyncKeyword)语义等价但传async 含空格会返回false。测试同时验证了两种参数形式对存在与不存在两种情况的结果modifierableNodeTests.ts。切换修饰符toggleModifiertoggleModifier是修改类 API 中最常用、也最省心的一个。它根据当前状态自动决定添加或删除functionDeclaration.toggleModifier(async); functionDeclaration.toggleModifier(async, false); // 显式指定false 表示确保移除行为规则如下不传第二个参数若当前没有该修饰符则添加有则移除传true确保存在幂等添加传false确保移除幂等删除。源码ModifierableNode.ts先解析默认值再委托给内部方法toggleModifier(text: ModifierTexts, value?: boolean) { if (value null) value !this.hasModifier(text); if (value) this.addModifier(text); else this.removeModifier(text); return this; }toggleModifier返回this便于链式调用。测试用例完整覆盖了四象限添加/移除 × 显式/隐式并验证了两个重要边界切换时不会破坏 JSDoc 注释对/** test */declare function Test {}执行toggleModifier(declare, false)后文本变为/** test */function Test {}切换时不会破坏行注释对// test\ndeclare function Test {}执行同样操作后保留// test行。见 modifierableNodeTests.ts。底层实现addModifier 与 removeModifieraddModifier与removeModifier是toggleModifier的内部支撑官方文档将它们标记为internalModifierableNode.ts意味着它们属于实现细节、不鼓励直接调用。但理解其机制有助于把握toggleModifier的行为。添加时自动排序addModifier并非简单地把关键字拼在节点开头而是依据getAddAfterModifierTexts定义的修饰符顺序规则把新修饰符插入到正确位置ModifierableNode.ts。核心规则可归纳为export总是最先出现无前置要求default、const跟在export之后declare跟在export、default之后static跟在public/protected/private之后override跟在作用域关键字与static之后abstract需放在export/default/declare以及作用域、static、override之后async、readonly紧随abstract之后再追加in在const之后out在const、in之后accessor排在作用域、declare、override、static、abstract、readonly之后。测试演示了这种自动排序效果对class Identifier {}依次addModifier(abstract)、addModifier(export)得到export abstract class Identifier {}依次添加export、abstract、declare得到export declare abstract class Identifier {}modifierableNodeTests.ts。此外addModifier还有两个细节幂等性若节点已存在同文本修饰符直接返回已有节点不会重复添加测试验证export加两次仍只有一个正确处理装饰器与 JSDoc插入位置计算会跳过开头的装饰器语法列表与 JSDoc 注释节点因此对dec class Identifier {}加export得到dec export class Identifier {}对带/** Test */的声明同理ModifierableNode.ts。删除时的注释安全removeModifier通过removeChildren实现ModifierableNode.ts。当该节点只有一个修饰符时会连同整个SyntaxList修饰符列表节点一起移除并启用removeFollowingSpaces: true清理多余空格同时避免误删前后的注释。这正是前面测试中断言注释得以保留的原因。基于修饰符的高级封装isAsync、setScope 等ModifierableNode的实用价值还体现在它是多个高阶 mixin 的地基。以下两组 API 的内部实现全部依赖修饰符操作可作为你在业务代码中复用“修饰符能力”的参考范式。异步相关AsyncableNodeAsyncableNode.ts 声明Node ModifierableNode为扩展基座提供了更语义化的接口functionDeclaration.isAsync(); // 是否 async functionDeclaration.getAsyncKeyword(); // async 关键字节点或 undefined functionDeclaration.getAsyncKeywordOrThrow(); // 找不到即抛错 functionDeclaration.setIsAsync(true); // 显式设置异步其实现全部委托给修饰符 APIisAsync()即hasModifier(SyntaxKind.AsyncKeyword)getAsyncKeyword()即getFirstModifierByKind(SyntaxKind.AsyncKeyword)setIsAsync(value)即toggleModifier(async, value)。作用域相关ScopedNode / ScopeableNode作用域关键字public/protected/private是修饰符中最典型的应用。ts-morph 提供了 Scope.ts 枚举export enum Scope { Public public, Protected protected, Private private, }ScopedNode.ts 与 ScopeableNode.ts 均基于ModifierableNode实现getScope()/setScope(scope)/hasScopeKeyword()。其中setScopeForNode正是通过多次调用toggleModifier完成作用域切换export function setScopeForNode(node: Node ModifierableNode, scope: Scope | undefined) { node.toggleModifier(public, scope Scope.Public); // 始终显式 node.toggleModifier(protected, scope Scope.Protected); node.toggleModifier(private, scope Scope.Private); }而getScopeForNode则借助ts.getCombinedModifierFlags位运算判断当前作用域ScopeableNode.ts。这条能力链同时展示了本仓库中getCombinedModifierFlags()的通用入口定义在 Node.ts可用于获取节点全部修饰符的组合标志位。实战示例给类方法批量设置与切换修饰符综合以上 API下面给出一个完整的实操场景——把类中所有方法设为async并确保其为public可见import { Project, SyntaxKind, Scope } from ts-morph; const project new Project({ useInMemoryFileSystem: true }); const sourceFile project.createSourceFile( src/example.ts, class Service { doWork(): void {} }, ); const classDecl sourceFile.getClasses()[0]; for (const method of classDecl.getMethods()) { // 1. 查询按文本与语法类型判断 console.log(method.hasModifier(async)); // false console.log(method.getFirstModifierByKind(SyntaxKind.AsyncKeyword)); // undefined // 2. 切换无则添加返回 this 便于链式 method.toggleModifier(async); // 添加 async method.setScope(Scope.Public); // 显式 public // 3. 校验 console.log(method.getModifiers().map(m m.getText())); // [public, async] console.log(method.hasModifier(SyntaxKind.PublicKeyword)); // true } console.log(sourceFile.getFullText()); // class Service { public async doWork(): void {} }输出结果符合预期async被自动插入到public之后符合getAddAfterModifierTexts中“async位于作用域关键字之后”的排序规则。小结修饰符是修饰其他节点的关键字节点private、async、export等都是典型例子只有混入ModifierableNode的节点才能调用相关 API覆盖类成员、函数、变量、模块、接口、枚举、类型参数等绝大多数声明读取与查询用getModifiers()、getFirstModifierByKind()、getFirstModifierByKindOrThrow()、hasModifier()支持文本或SyntaxKind两种入参修改用toggleModifier(text, value?)幂等且自动按规范排序、保留装饰器与注释内部addModifier/removeModifier为internal实现更语义化的isAsync()、setScope()等 API 均构建在修饰符操作之上可直接作为业务代码的参考范式。相关实现与测试可继续研读ModifierableNode.ts、AsyncableNode.ts、ScopeableNode.ts、modifierableNodeTests.ts。赞分享开发工具【免费下载链接】ts-morphTypeScript Compiler API wrapper for static analysis and programmatic code changes.项目地址https://gitcode.com/gh_mirrors/ts/ts-morph点击查看免费下载相关推荐ts-morph 环境声明Ambient节点处理完全指南isAmbient、declare 关键字检测与增删ts morph 环境声明Ambient节点处理完全指南isAmbient、declare 关键字检测与增删 摘要导读 在 TypeScript 中开发工具TypeScript 映射类型修饰符Mapped Type Modifiers完全指南readonly、readonly、-readonly 与可选性变换TypeScript 映射类型修饰符Mapped Type Modifiers完全指南readonly、readonly、 readonly 与可选性变文档教程Imba 事件修饰符Event Modifiers完全指南内置修饰符、自定义修饰符与处理链机制Imba 事件修饰符Event Modifiers完全指南内置修饰符、自定义修饰符与处理链机制 事件修饰符Event Modifiers是 Imba编程语言编译器语言运行时上一篇Apache Pulsar PIP-284 深度解析使用 TableView 重构 Topic Policies 底层实现下一篇Madison平台开发指南从零开始搭建你的立法协作系统创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考