如何在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
相关产品推荐
相关产品推荐

