TypeScript中修改Catalog对象内数组的类型错误修复问询
TypeScript工具函数类型错误修复:push参数类型不匹配
问题背景
需要修改CatalogEntity对象内的数组,因此编写了appendEntity工具函数用于新增或更新数组元素,但在调用push方法时出现类型不匹配错误。
定义代码
export interface CatalogEntity { items: CatalogItem[] categories: CatalogCategory[] modifiers: CatalogModifier[] discounts: CatalogDiscount[] taxes: CatalogTax[] salesTypes: CatalogSalesType[] } export type CatalogEntityKey = keyof CatalogEntity export const catalog: CatalogEntity = { items: [], categories: [], modifiers: [], discounts: [], taxes: [], salesTypes: [], }
工具函数代码
export type ArrayType<T> = T extends (infer U)[] ? U : never export function appendEntity< K extends CatalogEntityKey, V extends ArrayType<CatalogEntity[K]> >(key: K, value: V): void { const index = catalog[key].findIndex(m => m.id === value.id) if (index >= 0) catalog[key][index] = value else catalog[key].push(value) }
错误信息
执行catalog[key].push(value)时触发类型错误:
Argument of type 'CatalogModifier | CatalogItem | CatalogCategory | CatalogDiscount | CatalogTax | CatalogSalesType' is not assignable to parameter of type 'CatalogModifier & CatalogItem & CatalogCategory & CatalogDiscount & CatalogTax & CatalogSalesType'. Type 'CatalogModifier' is not assignable to type 'CatalogModifier & CatalogItem & CatalogCategory & CatalogDiscount & CatalogTax & CatalogSalesType'. Type 'CatalogModifier' is missing the following properties from type 'CatalogItem': variants, modifiers, taxests(2345)
尝试类型断言catalog[key].push(value as V)后仍报错:
Argument of type 'V' is not assignable to parameter of type 'CatalogModifier & CatalogItem & CatalogCategory & CatalogDiscount & CatalogTax & CatalogSalesType'. Type 'ArrayType<CatalogEntity[K]>' is not assignable to type 'CatalogModifier & CatalogItem & CatalogCategory & CatalogDiscount & CatalogTax & CatalogSalesType'. Type 'unknown' is not assignable to type 'CatalogModifier & CatalogItem & CatalogCategory & CatalogDiscount & CatalogTax & CatalogSalesType'. Type 'unknown' is not assignable to type 'CatalogModifier'. Type 'CatalogModifier | CatalogItem | CatalogCategory | CatalogDiscount | CatalogTax | CatalogSalesType' is not assignable to type 'CatalogModifier & CatalogItem & CatalogCategory & CatalogDiscount & CatalogTax & CatalogSalesType'. Type 'CatalogModifier' is not assignable to type 'CatalogModifier & CatalogItem & CatalogCategory & CatalogDiscount & CatalogTax & CatalogSalesType'. Type 'CatalogModifier' is missing the following properties from type 'CatalogItem': variants, modifiers, taxes Type 'ArrayType<CatalogEntity[K]>' is not assignable to type 'CatalogModifier'. Type 'V' is not assignable to type 'CatalogModifier'. Type 'ArrayType<CatalogEntity[K]>' is not assignable to type 'CatalogModifier'. Type 'unknown' is not assignable to type 'CatalogModifier'. Type 'CatalogModifier | CatalogItem | CatalogCategory | CatalogDiscount | CatalogTax | CatalogSalesType' is not assignable to type 'CatalogModifier'. Property 'options' is missing in type 'CatalogItem' but required in type 'CatalogModifier'.ts(2345)
问题原因
TypeScript无法正确推断catalog[key]的具体数组类型,由于泛型K是联合类型,CatalogEntity[K]被错误解析为所有数组元素类型的交叉类型,导致push方法的参数类型校验失败。
修复方案
方案一:类型断言明确数组元素类型
将catalog[key]断言为对应元素类型的数组,让TypeScript识别正确的元素类型:
export function appendEntity< K extends CatalogEntityKey, V extends ArrayType<CatalogEntity[K]> >(key: K, value: V): void { const array = catalog[key] as V[] const index = array.findIndex(m => m.id === value.id) if (index >= 0) array[index] = value else array.push(value) }
方案二:优化泛型约束,使用映射类型关联键与元素
通过映射类型直接绑定键和对应的元素类型,避免类型分发错误:
// 定义键到元素类型的映射关系 type CatalogEntityElement = { [K in CatalogEntityKey]: ArrayType<CatalogEntity[K]> } export function appendEntity<K extends CatalogEntityKey>( key: K, value: CatalogEntityElement[K] ): void { const array = catalog[key] const index = array.findIndex(m => m.id === value.id) if (index >= 0) array[index] = value else array.push(value) }
方案三:类型守卫收窄类型(适合复杂场景)
通过类型守卫逐个明确数组类型,代码量较大但无需断言:
export function appendEntity< K extends CatalogEntityKey, V extends ArrayType<CatalogEntity[K]> >(key: K, value: V): void { if (key === 'items') { const array = catalog[key] as CatalogItem[] const index = array.findIndex(m => m.id === value.id) index >= 0 ? (array[index] = value as CatalogItem) : array.push(value as CatalogItem) } else if (key === 'categories') { const array = catalog[key] as CatalogCategory[] const index = array.findIndex(m => m.id === value.id) index >= 0 ? (array[index] = value as CatalogCategory) : array.push(value as CatalogCategory) } // 依次处理其他key分支... }
说明
方案一和方案二是最简洁的实现方式,其中方案二通过映射类型让TypeScript清晰关联键与元素类型,无需类型断言,是更优雅的解决方案。
内容的提问来源于stack exchange,提问作者Ardy Febriansyah
相关产品推荐
相关产品推荐

