You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过成员关系筛选元组并实现TypeScript类型收窄?

TypeScript中基于元组值筛选数组并实现精确类型推断

给定一个通过const断言定义的对象数组,其中每个对象包含嵌套的只读元组:

const foo = [
  { id: 't1', values: ['a', 'b'] },
  { id: 't2', values: ['a', 'c'] },
  { id: 't3', values: ['b', 'c'] },
] as const;

我们需要筛选出values包含'a'的元素,同时让返回结果的类型精确排除不符合条件的项(比如t3)。

原生filter的问题

直接使用filter会出现类型错误:只读元组的includes方法参数类型被限制为元组中的字面量类型,传入'a'会触发类型不匹配:

// 类型错误:类型'"a"'的参数不能赋给类型'"b"'的参数
var objsWithA = foo.filter(({ values }) => values.includes('a'));

如果用as any或as never绕过类型错误,虽然运行时结果正确,但返回类型仍然是原数组的完整类型,无法排除t3:

// 类型为readonly [{id: "t1", ...}, {id: "t2", ...}, {id: "t3", ...}]
var objsWithA = foo.filter(({ values }) => values.includes('a' as never));

解决方案:自定义类型守卫

要实现精确的类型推断,需要编写类型守卫函数,让TypeScript能够识别出筛选后元素的具体类型:

针对当前场景的类型守卫

type FooElement = typeof foo[number];

// 类型守卫:判断对象的values元组是否包含指定值
function hasValue<T extends FooElement, V extends FooElement['values'][number]>(
  obj: T,
  value: V
): obj is T & { values: readonly [V, ...any[]] | readonly [...any[], V] } {
  return obj.values.includes(value as any);
}

使用类型守卫筛选数组

const objsWithA = foo.filter(obj => hasValue(obj, 'a'));

此时objsWithA的类型会被精确推断为:

readonly [{
    readonly id: "t1";
    readonly values: readonly ["a", "b"];
}, {
    readonly id: "t2";
    readonly values: readonly ["a", "c"];
}]

通用版本的类型守卫

如果需要复用在其他类似结构的数组上,可以写一个不依赖具体类型的通用版本:

function includesValue<T extends { values: readonly U[] }, U>(
  obj: T,
  value: U
): obj is T & { values: readonly [...any[], U, ...any[]] } {
  return obj.values.includes(value as any);
}

原理说明

类型守卫通过obj is ...语法告诉TypeScript:当函数返回true时,传入的obj会被缩小为特定的子类型。结合const断言的字面量类型,TypeScript可以准确识别出哪些元素符合筛选条件,从而生成精确的返回类型。

内容的提问来源于stack exchange,提问作者lonewarrior556

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 20:05:25