Angular compiler-cli ErrorCode 错误码体系全解析:枚举定义、NG 编码机制与诊断实现
【免费下载链接】angularDeliver web apps with confidence 🚀项目地址: https://gitcode.com/GitHub_Trending/an/angular
本文以 Angular 仓库公开 API 清单 error_code.api.md 中的ErrorCode枚举为骨架,结合其底层实现文件packages/compiler-cli/src/ngtsc/diagnostics下的源码,系统梳理 Angular 模板编译器(ngtsc)与@angular/compiler-cli使用的全部错误码:它们的数值分布规律、各错误族(装饰器、组件/指令、NgModule、模板类型检查等)的含义,以及"内部错误码如何被编码成你看到的NGxxxx形式"的底层原理。读完本文,你将能根据控制台中的NG前缀错误码快速反查对应的枚举成员与触发场景,也能理解 Angular 编译器错误诊断从抛出到格式化输出的完整链路。
一、ErrorCode是什么:一份由 API Extractor 锁定的公开契约
ErrorCode是 Angular compiler-cli 中 ngtsc(新一代 Angular TypeScript 编译器)统一定义的诊断错误码枚举,被标注为@public(见 error_code.ts),并从诊断模块的公共出口 index.ts 中export {ErrorCode}导出。
error_code.api.md 则是通过 API Extractor 自动生成的API 报告文件,其头部声明 "Do not edit this file. It is a report generated by API Extractor",意味着它的作用是锁死ErrorCode的公开成员名称与数值,防止在演进过程中意外改号或改名导致下游工具(如 Angular Language Service、编辑器插件、自定义扩展诊断)失配。
该文件展示的枚举拥有 120 个成员,数值覆盖约 20 个语义区间。源码中每个成员往往带有 JSDoc 注释说明触发条件,甚至配有错误模板示例(见 error_code.ts)。因此,本文后面每一个错误族的小结都可以视为"枚举 + 官方注释"的精读结果。
二、数值分布规律:读懂错误码的"分区"
从整体看,ErrorCode的数值按编译器处理阶段做了分区,区间首位数字即代表诊断来源:
| 数值区间 | 覆盖领域 | 示例 |
|---|---|---|
| -1001 / 1002 ~ 1100 | 装饰器(Decorator)校验与 initializer API 使用 | DECORATOR_COLLISION = 1006 |
| -2003 / 2001 ~ 2028 | 组件 / 指令 / 宿主指令元数据处理 | COMPONENT_MISSING_TEMPLATE = 2001 |
| -3003 / 3001 ~ 3004 | 引用解析、符号导出与导入生成 | SYMBOL_NOT_EXPORTED = 3001 |
| 4001 ~ 4006 | 编译器配置(angularCompilerOptions)校验 | CONFIG_FLAT_MODULE_NO_INDEX = 4001 |
| 5001 ~ 5002 | 宿主表达式与模板解析错误 | TEMPLATE_PARSE_ERROR = 5002 |
| -6100 / 6001 ~ 6009 | NgModule 语义分析 | NGMODULE_INVALID_DECLARATION = 6001 |
| -8001 ~ -8003 / 8004 ~ 8029 | 模板语义、Schema 校验、@defer/@let等模板特性 | MISSING_PIPE = 8004 |
| 8101 ~ 8118 | 模板类型检查与模板诊断 | INVALID_BANANA_IN_BOX = 8101 |
| 8900 ~ 8901 | 内联类型检查块 / 类型构造器生成 | INLINE_TCB_REQUIRED = 8900 |
| 9001 | Injectable 元数据冲突 | INJECTABLE_DUPLICATE_PROV = 9001 |
| 10001 ~ 10002 | 建议类(suggestion)诊断,非 Error 类别 | SUGGEST_STRICT_TEMPLATES = 10001 |
| 11001 / 11003 / 11100 ~ 11102 | 局部编译模式(local compilation)与具名@defer依赖 | LOCAL_COMPILATION_UNRESOLVED_CONST = 11001 |
源码还保留了一条编号纪律:6999 曾分配给NGMODULE_VE_DEPENDENCY_ON_IVY_LIB(View Engine 时代依赖 Ivy 库的旧错误),为避免混淆,该号码被明确注释为"不再复用"(见 error_code.ts)。这说明错误码一旦发布便被视作稳定标识,不会二次分配。
2.1 负数成员的用途
注意表中若干成员为负值(如-2009、-3003、-8001)。在 util.ts 中errorCodeWithGuideFromDiagnosticCode仅对负值错误码做指南(guide)查找,返回-absoluteErrorCode命中的成员;随后 addDiagnosticDetails 会向诊断消息追加Find more at {ERROR_DETAILS_PAGE_BASE_URL}/NG1001形式的官方错误指南链接(基础地址由 error_details_base_url.ts 依据版本号计算,预发布版指向next前缀域名)。测试用例对此给出了实证(format_compiler_error_spec.ts)。需要强调的是,不论枚举值是正是负,最终对外展示的错误码一律取绝对值(下一节详述),符号只是编译流水线内部使用的元信息。
三、从枚举值到NGxxxx:-99 标记编码机制
这是理解 Angular 编译器错误码最关键的一节。Angular 的诊断会复用 TypeScript 的ts.Diagnostic结构,而 TS 的格式化器会给每个诊断号硬编码TS前缀。为了让 Angular 自有的诊断显示成NG前缀,error_code.ts 采用了巧妙的"标记数"方案:
ERROR_CODE_MARKER = 99,即所有 Angular 诊断的数值都以99打头;ngErrorCode(code)计算出-(99 * 10^digits + |code|)作为真正的ts.Diagnostic.code。例如枚举成员DECORATOR_ARG_NOT_LITERAL = -1001,取绝对值后编码为-991001;DECORATOR_ARITY_WRONG = 1002编码为-991002(测试见 format_compiler_error_spec.ts);- 在诊断文本格式化阶段,
replaceTsWithNgInErrors用正则/(\u001b\[\d+m ?)TS-99(\d+: ?\u001b\[\d+m)/g把TS-99替换为NG,于是TS-991002显示为NG1002(util.ts)。
export const ERROR_CODE_MARKER = 99; export function ngErrorCode(code: ErrorCode): number { const absoluteCode = Math.abs(code); return -(ERROR_CODE_MARKER * 10 ** decimalDigits(absoluteCode) + absoluteCode); } export function formatCompilerErrorCode(code: number): string { return `NG${Math.abs(code)}`; }因此,你在构建日志或 IDE 里看到的NG1001、NG8111等,对应的ts.Diagnostic.code实际是-991001、-998111,而枚举原文分别是-1001/1002与8111。三者的换算关系可概括为:
- 枚举值:Angular 内部语义号(可能带符号);
- TS 诊断码:
-99拼接四位/五位补零绝对值(ngErrorCode产物); - 展示号:
NG+ 绝对值(formatCompilerErrorCode产物)。
这样设计的好处是让所有 Angular 错误码天然规避 TS 自带诊断的码段,并能在同一套 TS 管道中无损往返(反向解析由absoluteErrorCodeFromDiagnosticCode完成,先取绝对值、剥离99标记,见 util.ts)。
四、错误族详解(全量 120 个成员)
下面按错误族逐个给出成员、数值与含义。含义文字取自枚举源码注释与 API 报告,可直接作为"报错排查手册"使用。
4.1 装饰器与 initializer API(-1001 ~ 1100)
| 成员 | 值 | 触发场景 |
|---|---|---|
DECORATOR_ARG_NOT_LITERAL | -1001 | 装饰器的参数不是字面量(无法静态求值) |
DECORATOR_ARITY_WRONG | 1002 | 装饰器调用参数个数错误 |
DECORATOR_NOT_CALLED | 1003 | 装饰器没有被当作函数调用 |
DECORATOR_UNEXPECTED | 1005 | 出现了不被允许使用的装饰器 |
DECORATOR_COLLISION | 1006 | 类或类字段上存在互不兼容的装饰器组合 |
VALUE_HAS_WRONG_TYPE | 1010 | 装饰器元数据值的类型不符 |
VALUE_NOT_LITERAL | 1011 | 需要字面量处提供了非字面量 |
DUPLICATE_DECORATED_PROPERTIES | 1012 | 同一属性被重复装饰 |
INITIALIZER_API_WITH_DISALLOWED_DECORATOR | 1050 | initializer API(如input())同时叠加了不允许的装饰器,如字段同时使用@Input与input() |
INITIALIZER_API_DECORATOR_METADATA_COLLISION | 1051 | initializer API 与类装饰器元数据重复声明(如 signal input 又出现在@Directive({inputs})) |
INITIALIZER_API_NO_REQUIRED_FUNCTION | 1052 | 某 initializer API 不支持.required却被调用 |
INITIALIZER_API_DISALLOWED_MEMBER_VISIBILITY | 1053 | initializer API 用于不允许的访问修饰符成员(如private字段) |
DUPLICATE_BINDING_NAME | 1054 | 同一组件/指令的 inputs、outputs、models 间存在重复绑定名 |
INCORRECTLY_DECLARED_ON_STATIC_MEMBER | 1100 | Angular 特性(inputs、outputs、queries 等)被错误地声明在静态成员上 |
4.2 组件、指令、宿主指令与样式资源(-2003 ~ 2028)
| 成员 | 值 | 触发场景 |
|---|---|---|
COMPONENT_MISSING_TEMPLATE | 2001 | @Component缺少template/templateUrl |
PIPE_MISSING_NAME | 2002 | @Pipe缺少name |
PARAM_MISSING_TOKEN | -2003 | DI 构造参数缺少注入 token |
DIRECTIVE_MISSING_SELECTOR | 2004 | @Directive/@Component缺少 selector |
UNDECORATED_PROVIDER | 2005 | 未加装饰器的类被作为 provider 传入模块或指令 |
DIRECTIVE_INHERITS_UNDECORATED_CTOR | 2006 | 指令继承自"无 Angular 装饰器基类"的构造函数 |
UNDECORATED_CLASS_USING_ANGULAR_FEATURES | 2007 | 发现使用了 Angular 特性却没有装饰器的类 |
COMPONENT_RESOURCE_NOT_FOUND | 2008 | 组件无法解析外部资源(模板或样式文件) |
COMPONENT_INVALID_SHADOW_DOM_SELECTOR | -2009 | 组件使用ShadowDom封装但 selector 不合 shadow DOM 标签要求 |
COMPONENT_NOT_STANDALONE | 2010 | 组件带imports却未标记standalone: true |
COMPONENT_IMPORT_NOT_STANDALONE | 2011 | imports中的指令/管道不是 standalone |
COMPONENT_UNKNOWN_IMPORT | 2012 | imports中的类型既非指令/管道也非 NgModule |
HOST_DIRECTIVE_INVALID | 2013 | 编译器无法解析宿主指令元数据 |
HOST_DIRECTIVE_NOT_STANDALONE | 2014 | 宿主指令不是 standalone |
HOST_DIRECTIVE_COMPONENT | 2015 | 宿主指令被声明为组件(应是指令) |
INJECTABLE_INHERITS_INVALID_CONSTRUCTOR | 2016 | 带装饰器的类继承的基类构造函数与 Angular DI 不兼容 |
HOST_DIRECTIVE_UNDEFINED_BINDING | 2017 | 宿主指令为不存在的绑定设置别名 |
HOST_DIRECTIVE_CONFLICTING_ALIAS | 2018 | 宿主指令别名与既有绑定的公开名冲突 |
HOST_DIRECTIVE_MISSING_REQUIRED_BINDING | 2019 | 宿主指令定义未暴露其要求的绑定 |
CONFLICTING_INPUT_TRANSFORM | 2020 | 同一输入既配置了transform又有ngAcceptInputType_成员 |
COMPONENT_INVALID_STYLE_URLS | 2021 | 组件同时使用styleUrls与styleUrl |
COMPONENT_UNKNOWN_DEFERRED_IMPORT | 2022 | deferredImports中的类型不是组件/指令/管道 |
NON_STANDALONE_NOT_ALLOWED | 2023 | 开启strictStandalone时声明了standalone: false组件 |
MISSING_NAMED_TEMPLATE_DEPENDENCY | 2024 | 具名模板依赖未定义在组件源文件中 |
INCORRECT_NAMED_TEMPLATE_DEPENDENCY_TYPE | 2025 | 具名模板依赖类型错误(如把指令类当作组件) |
UNSUPPORTED_SELECTORLESS_COMPONENT_FIELD | 2026 | selectorless 场景下使用了不支持的@Component字段 |
COMPONENT_ANIMATIONS_CONFLICT | 2027 | 组件既使用animations属性又在模板中使用animate.enter/leave |
SERVICE_CONSTRUCTOR_DI | 2028 | @Service类使用了构造函数依赖注入 |
4.3 符号与导入生成(-3003 ~ 3004)
| 成员 | 值 | 触发场景 |
|---|---|---|
SYMBOL_NOT_EXPORTED | 3001 | 引用的符号未从目标文件导出 |
IMPORT_CYCLE_DETECTED | -3003 | 指令/管道之间会形成无法处理的循环导入(如 partial 编译模式) |
IMPORT_GENERATION_FAILURE | 3004 | 编译器无法为某个引用生成 import 语句 |
4.4 编译器配置(4001 ~ 4006)
| 成员 | 值 | 触发场景 |
|---|---|---|
CONFIG_FLAT_MODULE_NO_INDEX | 4001 | 扁平模块(flat module)缺少入口index文件 |
CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK | 4002 | 配置组合要求全量模板类型检查但未满足条件 |
CONFIG_EXTENDED_DIAGNOSTICS_IMPLIES_STRICT_TEMPLATES | 4003 | 配置了extendedDiagnostics却未开启strictTemplates |
CONFIG_EXTENDED_DIAGNOSTICS_UNKNOWN_CATEGORY_LABEL | 4004 | extendedDiagnostics中出现未知类别标签 |
CONFIG_EXTENDED_DIAGNOSTICS_UNKNOWN_CHECK | 4005 | extendedDiagnostics中出现未知的检查项名称 |
CONFIG_EMIT_DECLARATION_ONLY_UNSUPPORTED | 4006 | 不支持emitDeclarationOnly模式的配置组合 |
4.5 解析错误(5001 ~ 5002)
| 成员 | 值 | 触发场景 |
|---|---|---|
HOST_BINDING_PARSE_ERROR | 5001 | 宿主表达式解析失败(如宿主监听器/绑定里出现管道) |
TEMPLATE_PARSE_ERROR | 5002 | 编译器无法解析组件模板 |
4.6 NgModule 分析(-6100 / 6001 ~ 6009)
| 成员 | 值 | 触发场景 |
|---|---|---|
NGMODULE_INVALID_DECLARATION | 6001 | declarations中存在非法引用 |
NGMODULE_INVALID_IMPORT | 6002 | imports中出现非法类型 |
NGMODULE_INVALID_EXPORT | 6003 | exports中出现非法类型 |
NGMODULE_INVALID_REEXPORT | 6004 | exports中的类型既不在declarations也未导入 |
NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC | 6005 | 传入 NgModule 的ModuleWithProviders缺少泛型参数 |
NGMODULE_REEXPORT_NAME_COLLISION | 6006 | NgModule 导出多个同名指令/管道且需生成私有 re-export |
NGMODULE_DECLARATION_NOT_UNIQUE | 6007 | 指令/管道同时属于两个及以上 NgModule 的declarations |
NGMODULE_DECLARATION_IS_STANDALONE | 6008 | standalone 指令/管道被放进 NgModule 的declarations |
NGMODULE_BOOTSTRAP_IS_STANDALONE | 6009 | standalone 组件出现在 NgModule 的 bootstrap 列表 |
WARN_NGMODULE_ID_UNNECESSARY | -6100 | 声明id: module.id(编译器显式禁用的反模式) |
4.7 Schema 校验与模板语义(-8001 ~ -8003,8004 ~ 8029)
| 成员 | 值 | 触发场景 |
|---|---|---|
SCHEMA_INVALID_ELEMENT | -8001 | 元素名未通过 DOM Schema 校验 |
SCHEMA_INVALID_ATTRIBUTE | -8002 | 属性名未通过 DOM Schema 校验 |
MISSING_REFERENCE_TARGET | -8003 | #ref="target"找不到匹配指令 |
MISSING_PIPE | 8004 | 模板使用的管道在编译作用域中不存在 |
WRITE_TO_READ_ONLY_VARIABLE | 8005 | 对只读的模板变量赋值(如(click)="something = ...") |
DUPLICATE_VARIABLE_DECLARATION | 8006 | 模板变量重复声明(如*ngFor中let i与let i = index冲突) |
SPLIT_TWO_WAY_BINDING | 8007 | 双向绑定的输入与输出目标不一致 |
MISSING_REQUIRED_INPUTS | 8008 | 指令的必填输入没有被绑定 |
ILLEGAL_FOR_LOOP_TRACK_ACCESS | 8009 | @for的 track 表达式访问了不可用的变量 |
INACCESSIBLE_DEFERRED_TRIGGER_ELEMENT | 8010 | @defer触发器元素不存在或位于不同视图 |
CONTROL_FLOW_PREVENTING_CONTENT_PROJECTION | 8011 | 投影插槽被多根节点的控制流包裹,导致后代无法投影 |
DEFERRED_PIPE_USED_EAGERLY | 8012 | deferredImports的管道在@defer块外被使用 |
DEFERRED_DIRECTIVE_USED_EAGERLY | 8013 | deferredImports的指令/组件在@defer块外被使用 |
DEFERRED_DEPENDENCY_IMPORTED_EAGERLY | 8014 | deferredImports的依赖又被放进了普通imports |
ILLEGAL_LET_WRITE | 8015 | 表达式尝试向@let声明赋值 |
LET_USED_BEFORE_DEFINITION | 8016 | 在@let定义前读取该变量 |
CONFLICTING_LET_DECLARATION | 8017 | @let声明与同作用域其他符号冲突 |
UNCLAIMED_DIRECTIVE_BINDING | 8018 | selectorless 指令语法中的绑定未命中该指令任何 input/output |
DEFER_IMPLICIT_TRIGGER_MISSING_PLACEHOLDER | 8019 | 隐式触发器的@defer缺少@placeholder |
DEFER_IMPLICIT_TRIGGER_INVALID_PLACEHOLDER | 8020 | 隐式触发器的@placeholder配置非法(如多根节点) |
DEFER_TRIGGER_MISCONFIGURATION | 8021 | @defer定义了不可达/冗余的触发器组合 |
FORM_FIELD_UNSUPPORTED_BINDING | 8022 | FormField指令上存在不支持的绑定 |
MULTIPLE_MATCHING_COMPONENTS | -8023 | 编译作用域中多个组件匹配模板中同一元素 |
CONFLICTING_HOST_DIRECTIVE_BINDING | -8024 | 宿主指令 input/output 以同名重复暴露 |
FOREIGN_COMPONENT_UNSUPPORTED_BINDING | 8025 | 外来组件(foreign component)节点上有不支持的 Angular 绑定 |
INVALID_CONTENT_PLACEMENT | 8026 | @content块不是外来组件的直接子节点 |
FOREIGN_COMPONENT_CONTENT_UNNECESSARY_FOR_CHILDREN | 8027 | @content块命名为children(应隐式传递,命名多余) |
CONFLICTING_CONTENT_DECLARATION | 8028 | 同一外来组件声明多个同名@content块 |
CONFLICTING_CONTENT_AND_PROPERTY | 8029 | @content块名与父外来组件上的输入绑定冲突 |
4.8 模板类型检查与信号语义(8101 ~ 8118)
| 成员 | 值 | 触发场景 |
|---|---|---|
INVALID_BANANA_IN_BOX | 8101 | 双向绑定语法写反(<div ([foo])="bar" />,括号包在方括号外层) |
NULLISH_COALESCING_NOT_NULLABLE | 8102 | ??左侧类型不含null/undefined |
MISSING_CONTROL_FLOW_DIRECTIVE | 8103 | 使用*ngIf等控制流指令但未导入CommonModule |
TEXT_ATTRIBUTE_NOT_BINDING | 8104 | 文本属性(如class.blue="true")不会按绑定处理 |
MISSING_NGFOROF_LET | 8105 | 使用NgForOf却忘了写let(如*ngFor="item of items") |
SUFFIX_NOT_SUPPORTED | 8106 | 绑定后缀不受支持(如[attr.width.px]) |
OPTIONAL_CHAIN_NOT_NULLABLE | 8107 | ?.左侧类型不含null/undefined |
SKIP_HYDRATION_NOT_STATIC | 8108 | ngSkipHydration被当作绑定使用(应为静态属性) |
INTERPOLATED_SIGNAL_NOT_INVOKED | 8109 | 模板插值中的信号函数未加括号调用 |
UNSUPPORTED_INITIALIZER_API_USAGE | 8110 | initializer API 被用在非初始化上下文中(如普通函数内返回input()) |
UNINVOKED_FUNCTION_IN_EVENT_BINDING | 8111 | 事件绑定写了(click)="myFunc"却未调用 |
UNUSED_LET_DECLARATION | 8112 | 模板中@let声明从未被使用 |
UNUSED_STANDALONE_IMPORTS | 8113 | @Component.imports里的符号在模板中未使用 |
UNPARENTHESIZED_NULLISH_COALESCING | 8114 | 空值合并与&&/||混用且未加括号 |
UNINVOKED_TRACK_FUNCTION | 8115 | @for的 track 函数未被调用(应track fn(item)) |
MISSING_STRUCTURAL_DIRECTIVE | 8116 | 模板使用了未导入的结构型指令 |
UNINVOKED_FUNCTION_IN_TEXT_INTERPOLATION | 8117 | 文本插值中函数未加括号({{ firstName }}) |
FORBIDDEN_REQUIRED_INITIALIZER_INVOCATION | 8118 | input.required()等在禁止上下文(属性初始化器、构造函数)中被调用 |
4.9 其余专用码段(8900 ~ 11102)
| 成员 | 值 | 触发场景 |
|---|---|---|
INLINE_TCB_REQUIRED | 8900 | 类型检查需生成内联 type check block,但当前环境不支持 |
INLINE_TYPE_CTOR_REQUIRED | 8901 | 类型检查需为指令/组件生成内联类型构造器,但环境不支持 |
INJECTABLE_DUPLICATE_PROV | 9001 | Injectable 已存在ɵprov属性 |
SUGGEST_STRICT_TEMPLATES | 10001 | 建议开启strictTemplates以利用 Angular Language Service 全部能力 |
SUGGEST_SUBOPTIMAL_TYPE_INFERENCE | 10002 | 结构型指令可提供高级类型收窄,但当前类型检查配置无法参与推断 |
LOCAL_COMPILATION_UNRESOLVED_CONST | 11001 | 局部编译中需静态解析来自编译单元外部的 const(如作为装饰器参数) |
LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION | 11003 | 局部编译暂不支持的表达式/语法 |
DEFER_BLOCK_MISSING_NAME_PARAMETER | 11100 | @defer缺少name参数但deferredImports是对象 |
DEFER_BLOCK_UNKNOWN_NAME_PARAMETER | 11101 | @defer的name参数不匹配deferredImports中的任何条目 |
DEFER_BLOCK_INVALID_NAME_PARAMETER | 11102 | @defer指定了name参数但deferredImports不是对象 |
特别说明:源码注释明确指出10XXX 码段专门保留给非ts.DiagnosticCategory.Error类别的诊断(error_code.ts),这类诊断由编译器在 Language Service 等工具的要求下生成,因此在常见构建报错中不出现。
五、错误码如何被生产与消费:诊断抛出的底层链路
仅知道枚举含义还不够。ErrorCode真正发挥作用,是在 error.ts 提供的工具函数中,把"枚举码 + TS AST 节点 + 消息"组装成ts.Diagnostic。
致命诊断FatalDiagnosticError:当 ngtsc 在分析阶段遇到无法继续的错误时,会直接抛出该异常,内部保存错误码、ts.Node、消息与可选的关联信息(error.ts)。其toDiagnostic()调用makeDiagnostic完成转换。makeDiagnostic的核心逻辑是:
export function makeDiagnostic( code: ErrorCode, node: ts.Node, messageText: string | ts.DiagnosticMessageChain, relatedInformation?: ts.DiagnosticRelatedInformation[], category: ts.DiagnosticCategory = ts.DiagnosticCategory.Error, ): ts.DiagnosticWithLocation { node = ts.getOriginalNode(node); return { category, code: ngErrorCode(code), file: ts.getOriginalNode(node).getSourceFile(), start: node.getStart(undefined, false), length: node.getWidth(), messageText, relatedInformation, }; }关键点:最终写入ts.Diagnostic.code的并非枚举原值,而是经过ngErrorCode编码后的-99 标记数值(第 3 节已推导)。同时makeDiagnostic会回溯到原始节点(ts.getOriginalNode),保证经过中间代码变换后,报错位置仍指向开发者手写的源码。
配套工具还包括makeDiagnosticChain/makeRelatedInformation(构造多级消息链与关联位置信息)、addDiagnosticChain(把补充说明追加进消息链)、isFatalDiagnosticError(运行时类型守卫)与isLocalCompilationDiagnostics(识别局部编译相关错误,供 g3 等 1P 环境附加额外信息),全部从 index.ts 导出。
六、错误码的消费方:格式化、指南链接与外部工具
ErrorCode生态的另外三个消费场景:
- 展示格式化:
formatCompilerErrorCode(code)返回NG${Math.abs(code)}。测试验证DECORATOR_ARG_NOT_LITERAL与DECORATOR_ARITY_WRONG分别格式化为NG1001、NG1002(format_compiler_error_spec.ts),与开发者日常在终端看到的错误号完全一致。 - 错误指南跳转:
addDiagnosticDetails向消息文本追加Find more at <base>/NG1001形式提示,测试断言消息拼接结果(同一 spec 的第三个用例)。指南基础地址根据@angular/compiler的VERSION计算:正式版指向v<major>.angular.dev/errors,预发布(-next/-rc)指向next.angular.dev/errors(error_details_base_url.ts)。 - 负值错误码的"是否有指南"判定:
errorCodeWithGuideFromDiagnosticCode先剥离99标记还原枚举码,再检查其是否命中负值成员(util.ts),从而决定是否值得为该诊断展示 error guide 链接。这也解释了为何 4.1~4.9 表格中部分成员刻意保留负数形态——它们是"拥有官方错误指南"的家族。
七、扩展阅读与排错建议
- 想查看
ErrorCode的完整 JSDoc 与模板示例,直接读枚举实现 error_code.ts,比 API 报告更详细; - 想确认某个诊断号与工具链的绑定关系是否被破坏,比对 API 报告 error_code.api.md 与源码是否一致(goldens 目录由 CI 校验);
- 想了解错误码格式化与指南链接的行为契约,阅读测试 format_compiler_error_spec.ts;
- 当遇到形如
NG8005的错误时,建议先反查上表定位错误族(8005 属模板语义区),再对照@for/@let/@defer/信号等对应特性写法修正模板,必要时开启strictTemplates与extendedDiagnostics让编译器在早期暴露更多 81xx 类问题。
总体而言,ErrorCode是理解 Angular 编译器"如何说话"的钥匙:掌握它的分区规则与 -99 编码机制,既能在排错时快速定位错误语义域,也能在开发依赖 compiler-cli 的工具链时正确使用ngErrorCode、formatCompilerErrorCode与诊断指南接口。
【免费下载链接】angularDeliver web apps with confidence 🚀项目地址: https://gitcode.com/GitHub_Trending/an/angular
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考