Angular升级后模板报错'controls'不存在于AbstractControl类型如何解决
改动原因
这个问题是Angular 14及以上版本引入的响应式表单严格类型校验导致的。升级后默认开启了表单类型检查,AbstractControl 基类的类型定义没有暴露controls属性,所以用点语法直接访问时TypeScript会抛出类型错误,和你升级依赖后的表现一致。
问题1:这类修改是否是必须的?
如果不调整项目的TS校验配置、也不给表单定义显式类型,原有写法确实无法通过类型校验,必须调整。但调整方式不止加['controls']索引签名这一种,有更合理的方案可以选。
问题2:保留原有写法的可行方案
方案1:给表单定义显式类型(推荐)
你只需要在组件中给你的表单声明明确的类型,TypeScript就能识别到FormArray的controls属性,原有写法完全不用改,示例如下:import { FormGroup, FormArray, FormControl, FormBuilder } from '@angular/forms'; // 定义表单内每个字段的类型 interface ItemForm { id: FormControl<number>; name: FormControl<string>; } interface MyFormType { items: FormArray<FormGroup<ItemForm>>; } // 声明表单时绑定类型 myForm: FormGroup<MyFormType>; constructor(private fb: FormBuilder) { this.myForm = this.fb.group<MyFormType>({ items: this.fb.array<FormGroup<ItemForm>>([]) }); }配置完成后模板中的
*ngFor="let item of myForm.controls.items.controls; let i=index"写法可以正常运行,不会报错,同时还能享受到完整的表单类型校验能力。方案2:关闭响应式表单严格类型校验(临时过渡用)
如果你暂时不想做全量类型适配,可以修改tsconfig.json的配置,关闭表单的严格类型检查,原有写法也能正常运行:{ "angularCompilerOptions": { "strictReactiveForms": false } }该方案会丢失响应式表单的类型安全保障,只建议作为临时过渡方案使用,不推荐长期开启。
方案3:组件内封装getter简化调用(可替代原有写法,维护性更高)
你可以在组件中封装一个getter直接返回FormArray的controls数组,模板写法更简洁,也不用改原有逻辑:get itemControls() { return (this.myForm.get('items') as FormArray).controls; }模板中直接修改为:
*ngFor="let item of itemControls; let i=index"后续如果表单结构调整,只需要修改组件内的getter即可,不用批量改模板代码。
内容的提问来源于stack exchange,提问作者Andrew Howard

