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

如何在if语句中从泛型类型推导其关联后续类型?

问题描述

我想实现一个可关联泛型类型及其后续类型的类型守卫,现有代码如下:

enum Fruit {
    Apple = "apple",
    Banana = "banana",
}

function isApple(fruit: Fruit): fruit is Fruit.Apple {
    return fruit === Fruit.Apple;
}

interface AppleDescription {
    color: string;
    radius?: number;
}

interface BananaDescription {
    length: number;
}

type FruitDescription<T extends Fruit> = T extends Fruit.Apple ? AppleDescription : BananaDescription;

function findFruitDescription<T extends Fruit>({ fruit }: { fruit: T }): FruitDescription<T>[] {
    if (fruit === Fruit.Apple && isApple(fruit)) {
        return [{ color: "red" }] as FruitDescription<T>[]; // 必须强制类型转换,否则无法识别类型
    } else {
        return [{ length: 10 }] as FruitDescription<T>[];
    }
}

function exemple<T extends Fruit>(fruit: T) {
    const fruitDescriptionList = findFruitDescription({ fruit });
    if (fruit === Fruit.Apple && isApple(fruit)) {
        console.log(fruit, fruitDescriptionList[0].color); // 这里无法自动推导fruitDescriptionList为AppleDescription[]
    }
}

目前在isApple(fruit)的分支里,fruitDescriptionList无法自动推导为AppleDescription[]。我知道可以写额外的数组类型守卫来解决,但想知道有没有办法不用写这个额外守卫。


解决方案

方法一:分支内直接做类型断言

借助已有的isApple守卫缩小fruit的类型后,直接对fruitDescriptionList做针对性的类型断言,无需额外写数组守卫:

function exemple<T extends Fruit>(fruit: T) {
    const fruitDescriptionList = findFruitDescription({ fruit });
    if (isApple(fruit)) {
        // 利用类型断言将数组类型锁定为AppleDescription[]
        const appleDescriptions = fruitDescriptionList as AppleDescription[];
        console.log(fruit, appleDescriptions[0].color);
    }
}

方法二:用函数重载替代泛型返回类型

重构findFruitDescription为重载函数,让TypeScript根据输入的具体Fruit类型直接返回对应描述数组,这样在调用时能自动推导类型,连原函数里的强制转换都可以去掉:

// 定义重载签名,明确输入输出的类型对应关系
function findFruitDescription(params: { fruit: Fruit.Apple }): AppleDescription[];
function findFruitDescription(params: { fruit: Fruit.Banana }): BananaDescription[];

// 实现签名
function findFruitDescription({ fruit }: { fruit: Fruit }) {
    if (isApple(fruit)) {
        return [{ color: "red" }];
    } else {
        return [{ length: 10 }];
    }
}

function exemple<T extends Fruit>(fruit: T) {
    const fruitDescriptionList = findFruitDescription({ fruit });
    if (isApple(fruit)) {
        console.log(fruit, fruitDescriptionList[0].color); // 自动推导为AppleDescription[]
    }
}

方法三:用映射类型强化类型关联

创建一个明确的映射类型,把Fruit枚举和对应描述类型绑定,让TypeScript能通过泛型缩小自动推导数组类型:

// 创建映射类型,明确每个Fruit对应的描述类型
type FruitToDescription = {
    [Fruit.Apple]: AppleDescription;
    [Fruit.Banana]: BananaDescription;
};

// 重构FruitDescription类型,基于映射类型定义
type FruitDescription<T extends Fruit> = FruitToDescription[T];

function findFruitDescription<T extends Fruit>({ fruit }: { fruit: T }): FruitDescription<T>[] {
    if (fruit === Fruit.Apple) {
        return [{ color: "red" }] as FruitDescription<T>[];
    } else {
        return [{ length: 10 }] as FruitDescription<T>[];
    }
}

function exemple<T extends Fruit>(fruit: T) {
    const fruitDescriptionList = findFruitDescription({ fruit });
    if (isApple(fruit)) {
        // 泛型T被缩小为Fruit.Apple,自动关联到AppleDescription
        console.log(fruit, (fruitDescriptionList as FruitToDescription[T])[0].color);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 03:15:16