TypeScript Either/Or泛型下ConfigTable联合类型赋值报TS2322
问题本质
这是TypeScript泛型的类型变异性规则导致的,属于对泛型赋值规则的理解偏差,需求本身可以通过正确的类型定义实现。
从报错信息可以判断,你的ConfigTable<T>接口中存在将T作为参数输入的成员(比如onRowClick: (row: T) => void这类函数属性),这种场景下泛型参数T处于逆变位置,ConfigTable<T>是不变类型——只有两边的T完全一致时,两个ConfigTable类型才能互相赋值,因此ConfigTable<GlobalRow>无法赋值给ConfigTable<MyRow | GlobalRow>。
可行实现方案
根据你的实际场景二选一即可:
- 如果你的需求是
workTable只能存两种配置其中一种,直接将类型定义为两个ConfigTable的联合类型,这也是最贴合「二选一」语义的写法:
public workTable: ConfigTable<MyRow> | ConfigTable<GlobalRow> = this.configMyRowTable; // 后续赋值完全符合类型校验 this.workTable = this.configGlobalTable;
使用时TS会自动对联合类型做收窄,不会丢失类型安全。
- 如果你确实需要
workTable持有支持联合行类型的配置,且确认ConfigTable<T>中的T只出现在输出位置(比如仅定义rows: T[]这类返回T的属性,没有接收T作为入参的方法),可以在TS4.7及以上版本用out关键字将泛型标记为协变:
// 标记T为协变参数 export interface ConfigTable<out T> { rows: T[]; // 其余仅输出T、不消费T的成员定义 }
标记协变后,ConfigTable<MyRow>、ConfigTable<GlobalRow>都可以合法赋值给ConfigTable<MyRow | GlobalRow>。注意如果接口中存在消费T的成员,不要使用该方案,会绕过TS类型检查引入安全漏洞。
原写法的误区
你之前默认认为「泛型参数是联合的父子关系时,泛型类型本身也自动兼容」,这个规则仅在泛型参数处于协变位置时成立;当T作为函数入参出现时属于逆变位置,此时泛型类型要求类型参数完全匹配,这是TS为了避免运行时类型错误做的强制约束。
内容的提问来源于stack exchange,提问作者Brandon Graze
相关产品推荐
相关产品推荐

