TypeScript中filter过滤联合类型数组后如何正确收窄元素类型
问题说明
该问题并非TypeScript特有场景:示例代码中A行在map方法内通过typeof做类型判断可正常运行,但如果取消B行的注释,TypeScript会抛出错误:
Property 'length' does not exist on type 'String | Number'
需要找到可行方案,让TypeScript能够识别数组经过filter操作完成过滤后,剩余元素全部为string类型。
示例代码如下:
const array_: (String| Number)[] = [99, "Hello, World!"]; array_.map((e) => {if (typeof e === "string") console.log(e.length)}) // A // array_.filter((e) => typeof e === "string").map((e) => {console.log(e.length)}) // B
原因说明
A行无报错是因为类型判断与属性访问逻辑同属一个函数作用域,TypeScript可通过控制流分析自动将if块内的e类型收窄为string。filter的回调属于独立函数作用域,TypeScript默认不会跨函数上下文推断过滤后的数组元素类型,因此无法自动将过滤结果的类型收窄为string[],最终抛出属性不存在的类型错误。
可行方案
核心是给filter的回调添加类型谓词(类型守卫),显式告诉TypeScript回调返回true时对应的元素类型,有两种常用写法:
- 内联标注类型谓词,适合单次使用的场景
在filter回调的参数后添加返回值类型e is string即可,修改后的B行代码可正常通过类型检查:array_.filter((e): e is string => typeof e === "string").map((e) => {console.log(e.length)}) - 封装通用类型守卫函数,适合逻辑复用的场景
如果同类判断逻辑需要多处使用,可以抽成独立的类型守卫函数:const isString = (val: unknown): val is string => typeof val === "string"; array_.filter(isString).map((e) => console.log(e.length))
补充提示:日常开发中推荐使用小写的原始类型
string、number,大写的String、Number是包装对象类型,非特殊场景不建议使用。
内容的提问来源于stack exchange,提问作者user1055604
相关产品推荐
相关产品推荐

