为何赋值看似兼容的FormArray时会出现TypeScript错误?
问题场景
从类型定义逻辑来看,我原本认为FormArray<FormControl<string>>可以赋值给FormArray<FormControl<string | null>>,但实际代码中触发了如下类型错误:
Type 'FormArray<FormControl<string>>' is not assignable to type 'FormArray<FormControl<string | null>>'. Type 'FormControl<string | null>' is not assignable to type 'FormControl<string>'. Type 'string | null' is not assignable to type 'string'. Type 'null' is not assignable to type 'string'.(2322)
我需要理解该错误的成因,同时困惑的是错误信息的逻辑似乎颠倒——第2行提到string | null无法赋值给string,这和我原本的赋值方向相反。
错误成因解析
泛型的逆变特性:
TypeScript中,像FormArray这类可修改的集合容器,其泛型参数遵循逆变规则,而非直觉中的协变。协变指子类型容器可以赋值给父类型容器(比如只读数组ReadonlyArray),但逆变则要求父类型容器才能赋值给子类型容器,反之不行。这里string是string | null的子类型,所以FormArray<FormControl<string>>(子类型容器)不能赋值给FormArray<FormControl<string | null>>(父类型容器)。FormControl的可写属性约束:
FormControl<string>的value属性只接受string,而FormControl<string | null>允许设置null值。如果允许前者赋值给后者,后续代码可能通过目标数组给原FormControl设置null,这会违反原类型的约束,导致类型不安全。错误信息的逻辑解读:
错误信息看似“颠倒”,是因为TypeScript在检查容器兼容性时,会验证:目标容器的元素类型是否能兼容原容器的元素类型(避免后续操作破坏原类型约束)。它本质是在提醒你:若允许这个赋值,就可能出现把支持null的操作应用到仅支持string的FormControl上,引发类型风险,所以才会反向报错FormControl<string | null>不能赋值给FormControl<string>。
解决思路
- 使用类型断言跳过检查(需确保后续不会给原
FormControl设置null):const strArray: FormArray<FormControl<string>> = new FormArray([new FormControl("test")]); const strOrNullArray: FormArray<FormControl<string | null>> = strArray as FormArray<FormControl<string | null>>; - 从创建阶段就指定支持
null的类型:const strOrNullArray: FormArray<FormControl<string | null>> = new FormArray([new FormControl<string | null>("test")]);
内容的提问来源于stack exchange,提问作者CShark

