ts-morph 修饰符(Modifiers)操作完全指南:读取、查询与切换类成员与声明的关键字
开发工具【免费下载链接】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),仅供参考

相关新闻

百度竞价点击软件测评:SEM投放中的风控博弈与账户安全

百度竞价点击软件测评:SEM投放中的风控博弈与账户安全

1. 竞价点击工具的市场乱象与测评初衷1.1 为什么突然想聊这个话题做SEM投放的朋友大概都有过这样的经历:账户跑了一周,展现量看着还行,点击量也凑合,但转化就是起不来。翻来覆去查关键词、调出价、改创意,折腾半天发现…

2026/10/10 1:52:46 阅读更多 →
软件测试面试题背后:面试官真正考察的是什么?

软件测试面试题背后:面试官真正考察的是什么?

软件测试面试题背后,面试官到底在面什么做了这么多年测试,也坐在面试官那头看过不少候选人。我发现一个规律:背得最熟的那批人,往往挂在最基础的问题上。因为面试题从来不是考你记没记住答案,而是考你有没有真正理解这…

2026/10/10 1:51:46 阅读更多 →
被 36 氪点名“逼 AI 当山顶洞人“:caveman 为什么连主流媒体都坐不住了?

被 36 氪点名“逼 AI 当山顶洞人“:caveman 为什么连主流媒体都坐不住了?

被 36 氪点名"逼 AI 当山顶洞人":caveman 为什么连主流媒体都坐不住了? 【免费下载链接】caveman 🪨 why use many token when few token do trick. Viral skill proxy for coding agents that cuts 65% of tokens by talking lik…

2026/10/10 1:51:46 阅读更多 →

最新新闻

pytorch深度学习实践(刘二大人)课堂代码作业——线性模型

pytorch深度学习实践(刘二大人)课堂代码作业——线性模型

一、课堂代码np.arange( ):用于创建一个等间隔的数组(类似于 Python 中的 range() 函数),其参数包括起始值、终止值和步长。np.arange() 函数生成的数组不包括终止值本身,即从起始值开始,依次加上步长&…

2026/10/10 8:14:47 阅读更多 →
计算机毕业设计之jsp基于Java的旅游网站的设计与实现

计算机毕业设计之jsp基于Java的旅游网站的设计与实现

系统根据现有的管理模块进行开发和扩展,采用面向对象的开发的思想和结构化的开发方法对旅游管理的现状进行系统调查。采用结构化的分析设计,该方法要求结合一定的图表,在模块化的基础上进行系统的开发工作。在设计中采用“自下而上”的思想&a…

2026/10/10 8:14:46 阅读更多 →
运维相关安装包下载地址统计(持续更新)

运维相关安装包下载地址统计(持续更新)

通用软件: jdk: https://www.oracle.com/java/technologies/downloads/#java17 (可更改最后面的版本号来下载自己所需要的版本) docker: https://download.docker.com/linux/centos/7/ , https://download.docker.com/linux/static/stable/ d…

2026/10/10 8:14:46 阅读更多 →
AlgoNote 算法通关手册:LeetCode 0973 最接近原点的 K 个点(K Closest Points to Origin)堆与优先队列解法全解析

AlgoNote 算法通关手册:LeetCode 0973 最接近原点的 K 个点(K Closest Points to Origin)堆与优先队列解法全解析

教程文档知识库 【免费下载链接】AlgoNote ⛽️「算法通关手册」:从零开始的「算法与数据结构」学习教程,200 道「算法面试热门题目」,1000 道「LeetCode 题目解析」,持续更新中! 项目地址: https://gitcod…

2026/10/10 8:14:46 阅读更多 →
GDAL 与 OGR Python 绑定使用陷阱全解析:异常机制、对象生命周期与经典踩坑案例

GDAL 与 OGR Python 绑定使用陷阱全解析:异常机制、对象生命周期与经典踩坑案例

GIS遥感数据工程 【免费下载链接】gdal GDAL is an open source MIT licensed translator library for raster and vector geospatial data formats. 项目地址: https://gitcode.com/gh_mirrors/gd/gdal 点击查看 免费下载 导读 GDAL 与 OGR 的 Python 绑定&#…

2026/10/10 8:14:46 阅读更多 →
UVa 11238 保龄球计分模拟:从规则拆解到动态规划实战

UVa 11238 保龄球计分模拟:从规则拆解到动态规划实战

看到UVa 11238这个题号,老 OJ 玩家应该会会心一笑:又是一道保龄球。题目名字里的Innumerous Bowling Games翻译过来是“数不清的保龄球比赛”,我第一次做的时候还真以为要枚举所有合法比赛,后来才发现核心考的是对保龄球计分规则的…

2026/10/10 8:13:45 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →