首页 > 解决方案 > 打字稿联合类型和推断不同的属性而无需额外的类型检查

问题描述

我在联合类型方面有点挣扎,我想知道打字稿是否可以在没有额外检查的情况下推断出值。假设我有这个接口设置(为简洁起见省略了 IBaseDiscount),其中值可以不同,但​​嵌套的折扣类型名称是固定的

interface IFlatDiscount extends IBaseDiscount {
    value:{ formatted: string; value: number };
    discountType: {
        name: DiscountType.flat;
        id: number;
    };
}

interface IOpenDiscount extends IBaseDiscount {
    value?: number;
    discountType: {
        name: DiscountType.open;
        id: number;
    };
}

export interface IPercentageDiscount extends IBaseDiscount {
    value: number;
    discountType: {
        name: DiscountType.percentage;
        id: number;
    };
}

export type IDiscount = IOpenDiscount | IPercentageDiscount | IFlatDiscount;

现在在我的代码中,当我尝试使用这些值时,我最终不得不执行以下操作

if (discount.discountType.name === DiscountType.flat && typeof discount.value === 'object) {
    // now my value is properly typed -- if I leave out the object check it doesnt know the correct type for the value
}

打字稿是否有适当的方法来根据 discountType.name 推断值,而不是在任何地方对值进行所有检查?

标签: typescript

解决方案


首先,您在这里进行了不必要的类型检查:

if (discount.discountType.name === DiscountType.flat && 
  typeof discount.value === 'object') {
}

以下就足够了:

if (typeof discount.value === 'object') {
  
}

如果要根据 确定接口discountType.name,打字稿无法从嵌套属性类型检查中推断类型。

基本上有两种方式:

  • 您使用“as”关键字(因为您确定类型正确):
switch(discount.discountType.name) {
  case DiscountType.flat:
    console.log((discount as IFlatDiscount).value.formatted);
    break;
  case DiscountType.open:
    console.log((discount as IOpenDiscount).value);
    break;
  case DiscountType.percentage:
    console.log((discount as IPercentageDiscount).value);
}
  • 更好的是,您可以使用泛型类型:
export enum DiscountType {
    flat,
    open,
    percentage
}


export interface IGenericDiscount<T extends DiscountType> {
    value: T extends DiscountType.flat 
        ? { formatted: string; value: number } 
        : 
            (T extends DiscountType.open 
                ? (number | undefined) 
                : number
            );
    discountType: {
        name: T;
        id: number;
    }
}

const genericDiscount: IGenericDiscount<DiscountType.open> = JSON.parse('{}');

console.log(genericDiscount.value);

打字稿游乐场


推荐阅读