如何用OR运算符筛选TypeScript可辨识联合类型?
TypeScript可辨识联合多类型筛选的类型推断问题
类型定义
type ClipBase = { duration: number; } type AudioClip = ClipBase & { type: "audio"; volume: number; } type VideoClip = ClipBase & { type: "video"; height: number; width: number; } type MediaClip = ClipBase & { type: "media"; url: string; } type Clip = VideoClip | AudioClip | MediaClip;
现有类型判断辅助函数
export function isType< GenericType extends string, Union extends { type: GenericType }, SpecificType extends GenericType, >(val: SpecificType) { return (obj: Union): obj is Extract<Union, { type: SpecificType }> => obj.type === val; }
单类型筛选(正常推断)
使用isType进行单一类型筛选时,TypeScript能正确推断出结果类型:
function getVideoClips(clips: Clip[]) { return clips.filter(isType("video")); // 类型推断为 VideoClip[] }
多类型筛选(类型推断失败)
但筛选可辨识联合的子集时,TypeScript无法自动推断出正确的联合类型,返回的仍是原类型Clip[]:
function getAudioAndMediaClips(clips: Clip[]) { return clips.filter(clip => isType("audio")(clip) || isType("media")(clip)); // 类型推断为 Clip[] }
解决方案:支持多类型的判断函数
我们可以编写一个支持批量类型判断的辅助函数,无需为每种类型组合单独编写逻辑:
export function isOneOfTypes< T extends { type: string }, K extends T["type"] >(types: readonly K[]) { return (obj: T): obj is Extract<T, { type: K }> => types.includes(obj.type as K); }
使用示例
function getAudioAndMediaClips(clips: Clip[]) { return clips.filter(isOneOfTypes(["audio", "media"])); // 类型推断为 (AudioClip | MediaClip)[] }
核心逻辑说明
- 泛型
K会捕获传入的所有类型值,通过Extract<T, { type: K }>从原联合类型中提取匹配的成员,生成新的联合类型。 - 传入只读数组
readonly K[]帮助TypeScript更精准地推断泛型参数。 obj.type as K是安全的类型断言,因为我们仅验证该值是否在传入的类型数组中。
内容的提问来源于stack exchange,提问作者Den
相关产品推荐
相关产品推荐

