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

如何编写保留前置类型信息的TypeScript复杂组合类型类型守卫?

为TypeScript复杂组合类型编写类型守卫的可行性

是否可以为包含多个与(&)、或(|)语句的TypeScript复杂组合类型编写类型守卫,以检测其中某种子类型的存在?

示例类型定义

interface Type1 { cat: string }
interface Type2 { dog: boolean }
interface Type3 { mouse: number }
interface Type4 { elephant: string | number }
type CombinatoryType = (Type1 & (Type2 | Type3)) | Type4;

预期的类型守卫行为

若存在检测Type2和Type4的类型守卫(分别命名为hasType2和hasType4),按以下顺序调用应得到对应类型推导结果:

function doSomething(data: CombinatoryType) {
    if (hasType2(data)) {
        // 'data' 现在的类型为: (Type1 & Type2) | Type4

        if (hasType4(data)) {
            // 'data' 现在的类型为: Type1 & Type2 & Type4
       }
    }
}

按相反顺序调用则应得到以下结果:

function doSomething(data: CombinatoryType) {
    if (hasType4(data)) {
        // 'data' 现在的类型为: (Type1 & (Type2 | Type3)) & Type4

        if (hasType2(data)) {
            // 'data' 现在的类型为: Type1 & Type2 & Type4
       }
    }
}

注意要点

  • 类型守卫不应丢失前置类型断言产生的类型信息
  • 类型守卫不能使用显式返回类型(如data is (Type1 & Type2) | Type4),因为组合类型可能非常冗长,且会干扰VSCode的类型推导

解决方案

可以通过泛型类型守卫实现需求,利用TypeScript的条件类型自动推导交集/并集的收缩结果,无需硬写冗长的目标类型。

实现通用类型守卫工具

先编写一个通用的类型守卫生成函数,用于检测对象是否包含目标类型的特征属性:

type KeysOfType<T> = keyof T;

function hasType<T extends object>(
    obj: unknown,
    key: KeysOfType<T>
): obj is T & typeof obj {
    return typeof obj === 'object' && obj !== null && key in obj;
}

针对示例的具体类型守卫

基于上述通用工具,生成hasType2和hasType4:

const hasType2 = (obj: unknown): obj is Type2 & typeof obj => 
    hasType<Type2>(obj, 'dog');

const hasType4 = (obj: unknown): obj is Type4 & typeof obj => 
    hasType<Type4>(obj, 'elephant');

验证推导效果

此时调用这两个类型守卫,TypeScript会自动保留前置的类型信息,实现预期的类型收缩:

function doSomething(data: CombinatoryType) {
    if (hasType2(data)) {
        // 推导类型:(Type1 & Type2) | Type4
        data.cat; // 正常访问Type1的属性
        data.dog; // 正常访问Type2的属性
        
        if (hasType4(data)) {
            // 推导类型:Type1 & Type2 & Type4
            data.elephant; // 正常访问Type4的属性
        }
    }

    if (hasType4(data)) {
        // 推导类型:(Type1 & (Type2 | Type3)) & Type4
        data.elephant; // 正常访问Type4的属性
        data.cat; // 正常访问Type1的属性
        
        if (hasType2(data)) {
            // 推导类型:Type1 & Type2 & Type4
            data.dog; // 正常访问Type2的属性
        }
    }
}

原理说明

  • 泛型T指定要检测的目标类型,key参数约束为T的键,确保检测的属性是目标类型的特征标识
  • 返回类型obj is T & typeof obj利用了TypeScript的类型交集特性:将当前obj的已有类型(可能是组合类型的一部分)与目标类型T取交集,自动保留前置的类型信息
  • 这种方式无需硬写复杂的组合类型,完全由TypeScript自动推导,避免了冗长代码和类型维护问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 22:05:27