文章目录

TypeScript类型保护函数在数组过滤中的类型推断

发布于 2026-05-18 03:21:45 · 浏览 24 次 · 评论 0 条

TypeScript类型保护函数在数组过滤中的类型推断

在处理包含多种类型的联合类型数组时,直接使用 Array.prototype.filter 往往无法达到预期的类型收窄效果。TypeScript 默认会将过滤后的数组类型推断为原始联合类型,导致后续访问特定类型属性时需要繁琐的类型断言。通过定义“类型保护函数”并利用“类型谓词”,可以让编译器自动识别并推断过滤后的数组为具体的子类型。

以下步骤将演示如何实现这一过程。


1. 定义基础类型与模拟数据

首先,创建一个包含可辨识属性的联合类型。可辨识属性(如 type)是区分不同类型的关键。

定义一个 Square(正方形)接口和一个 Circle(圆形)接口。

interface Square {
  kind: 'square';
  size: number;
}

interface Circle {
  kind: 'circle';
  radius: number;
}

type Shape = Square | Circle;

创建一个包含混合类型的数组 shapes

const shapes: Shape[] = [
  { kind: 'square', size: 10 },
  { kind: 'circle', radius: 5 },
  { kind: 'square', size: 20 },
];

2. 观察普通过滤的类型推断问题

尝试使用常规的箭头函数过滤出所有的 Square

调用 filter 方法筛选 kind 属性为 'square' 的元素。

const squares = shapes.filter((shape) => shape.kind === 'square');

// 尝试访问 size 属性
const totalSize = squares.reduce((acc, curr) => acc + curr.size, 0);

观察上述代码,你会发现虽然逻辑上 squares 数组中只包含 Square 对象,但 TypeScript 推断 squares 的类型依然是 Shape[]。在访问 curr.size 时,编译器可能会报错,提示 Circle 类型上不存在 size 属性,因为编译器认为数组中可能仍然存在 Circle


3. 编写类型保护函数

为了解决上述问题,需要编写一个返回值类型为“类型谓词”的函数。类型谓词的语法形式为 parameterName is Type,它告诉编译器:如果该函数返回 true,则参数必须是指定的类型。

声明一个函数 isSquare,接收 Shape 类型的参数,并将其返回值显式注解为 arg is Square

function isSquare(shape: Shape): shape is Square {
  return shape.kind === 'square';
}

在这个函数中,我们检查 shape.kind 是否等于 'square'。这个逻辑返回布尔值,但关键的类型魔法发生在返回类型注解 shape is Square 上。


4. 将类型保护函数应用于过滤

现在,将这个类型保护函数作为回调函数传递给 filter 方法。注意这里只需传递函数名,而不是调用它。

替换之前的箭头函数为 isSquare 函数。

const safeSquares = shapes.filter(isSquare);

此时,TypeScript 编译器会利用 isSquare 中的类型谓词进行推断。safeSquares 的类型被正确收窄为 Square[]

验证类型推断效果,直接访问特定属性不再需要类型断言。

// 此时 safeSquares 的类型为 Square[],编译器确信每个元素都有 size 属性
const safeTotalSize = safeSquares.reduce((acc, curr) => acc + curr.size, 0); 

5. 流程原理解析

为了更直观地理解类型推断的变化,可以通过以下流程查看编译器处理逻辑的变化。

graph LR A["输入: shapes: Shape[]"] --> B["调用 filter(isSquare)"] B --> C{isSquare 返回 true?} C -- 是 --> D["TypeScript 识别类型谓词"] D --> E["收窄类型为: Square[]"] C -- 否 --> F["过滤掉该元素"] F --> G["最终结果: 仅包含 Square 的数组"] E --> G

6. 对比总结

下表展示了普通箭头函数与类型保护函数在过滤数组时的核心区别。

特性 普通箭头函数 类型保护函数
语法示例 s => s.kind === 'square' (s): s is Square => s.kind === 'square'
返回类型 boolean s is Square (类型谓词)
过滤后类型 Shape[] (未改变) Square[] (成功收窄)
类型安全性 低,需手动断言 高,自动推断
代码可读性 逻辑直观 语义清晰(“它是Square吗?”)

7. 高级用法:处理复杂对象结构

如果数组元素可能是 nullundefined,也可以使用同样的技巧。

定义一个过滤掉空值的类型保护。

function isNotNull<T>(value: T | null): value is T {
  return value !== null;
}

const numbers: (number | null)[] = [1, 2, null, 4];

// 过滤后的类型被推断为 number[]
const validNumbers = numbers.filter(isNotNull);

通过这种方式,你可以复用 isNotNull 函数来处理任何可能包含空值的数组,极大地提升了代码的健壮性和可维护性。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文