TypeScript动态调用数组filter方法报错的原因及解决方案
TypeScript 动态访问对象属性调用数组filter方法类型报错
报错核心原因
Object.keys()天生返回string[]类型,即使手动将key断言为keyof typeof shapes,通过联合key索引对象得到的结果是三个不同元素类型数组的联合:{x: number}[] | {y: number}[] | {isFinished: boolean}[]- TypeScript 不支持直接调用联合类型上的方法:当访问联合类型的方法时,TS会将所有成员类型的同名方法做交叉合并,三个数组的
filter方法重载签名互不兼容,合并后得到的是多个函数签名的联合类型,无法直接调用,这就是报错的直接来源。 - 静态校验阶段TS无法确定运行时拿到的到底是哪一个数组,自然也无法推导filter回调参数的类型,和“值是不是数组”没有关系,核心是数组元素类型不统一导致的联合类型方法调用问题。
报错复现
报错提示信息:
(property) Array<T>.filter: { <S extends { x: number; }>(predicate: (value: { x: number; }, index: number, array: { x: number; }[]) => value is S, thisArg?: any): S[]; (predicate: (value: { x: number; }, index: number, array: { x: number; }[]) => unknown, thisArg?: any): { x: number; }[]; } | { ...; } | { ...; }
触发报错的代码:
const shapes ={ rectangles :[ {x :1} ], ellipses :[ {y :10} ], polygons :[ {isFinished :true} ] } Object.keys(shapes).forEach(shapeKey => { shapes[shapeKey as keyof typeof shapes].filter(shape => { // 此处抛出类型错误 }) })
可行修复方案
方案1:统一数组元素类型,给对象添加索引签名
如果业务逻辑中所有图形属性都可以抽象为统一的Shape类型,直接给shapes声明统一的类型,让索引访问得到确定的数组类型,从根源避免联合类型:
type Shape = { x?: number; y?: number; isFinished?: boolean; } const shapes: Record<string, Shape[]> = { rectangles: [{x: 1}], ellipses: [{y: 10}], polygons: [{isFinished: true}] } Object.keys(shapes).forEach(shapeKey => { shapes[shapeKey].filter(shape => { // shape类型为Shape,无类型报错 return true; }) })
方案2:调用方法前做类型断言
如果不希望修改原始对象的类型定义,可以在调用filter前将数组断言为兼容所有元素的联合数组类型:
const shapes ={ rectangles :[{x :1}], ellipses :[{y :10}], polygons :[{isFinished :true}] } type ShapeItem = {x:number} | {y:number} | {isFinished:boolean}; Object.keys(shapes).forEach(shapeKey => { const list = shapes[shapeKey as keyof typeof shapes] as ShapeItem[]; list.filter(shape => { // 可在回调内通过in操作符收窄具体类型 if ('x' in shape) return shape.x > 0; return true; }) })
方案3:按key分支做类型收窄
如果不同图形数组的过滤逻辑完全不同,可以通过判断key值帮TS收窄当前数组的类型,不需要额外断言:
const shapes ={ rectangles :[{x :1}], ellipses :[{y :10}], polygons :[{isFinished :true}] } Object.keys(shapes).forEach(shapeKey => { const list = shapes[shapeKey as keyof typeof shapes]; if (shapeKey === 'rectangles') { list.filter(shape => shape.x > 0); // shape自动推导为{x:number} } else if (shapeKey === 'ellipses') { list.filter(shape => shape.y > 5); // shape自动推导为{y:number} } else { list.filter(shape => shape.isFinished); // shape自动推导为{isFinished:boolean} } })
内容的提问来源于stack exchange,提问作者Nyi Nyi Hmue Aung
相关产品推荐
相关产品推荐

