枚举的联合与枚举不同吗?

时间:2019-03-22 08:31:36

标签: javascript typescript enums

我有一些共享许多属性的对象,但是有一个小的子集,它们是不同的。它们的ID区别于枚举。我想将这些类型输入为同一泛型的子类型,以便利用类型防护来了解哪些属性是可访问的。

示例:

enum ItemIDs {
    ITEM_TYPE_1,
    ITEM_TYPE_2
}

// Generic item with shared properties
interface GenericItem<ID, Data> {
    id: ID
    data: Data
}

// Specific items where the 'data' property can be different shapes
type SpecificItemOne = GenericItem<ItemIDs.ITEM_TYPE_1, { content: string }>
type SpecificItemTwo = GenericItem<ItemIDs.ITEM_TYPE_2, { amount: number }>

// Specific item is a union of all specific items
type SpecificItem = SpecificItemOne | SpecificItemTwo;

// Take item and test that typescript can work out what properties are available
// It works!
const testTypeGuard = (item: SpecificItem) => {
    if (item.id === ItemIDs.ITEM_TYPE_1) {
        item.data.content = ''
    } else if (item.id === ItemIDs.ITEM_TYPE_2) {
        item.data.amount = 0;
    }
    return item;
}

// Try to create item where ID can be any from ID enum
const breakTypeGuard = (id: ItemIDs, data: any) => {
    // Type 'ItemIDs' is not assignable to type 'ItemIDs.ITEM_TYPE_2'.
    // WHY
    testTypeGuard({ id, data });
}

interactive on the ts site

似乎是在说它不能将所有枚举值分配给特定的子类型。我不明白为什么会出现这个问题,因为它与其他类型的联合在一起确实接受了所有枚举值。

我在做什么错了?

感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

问题在于,当您发送{ id, data }作为参数时,这被视为对象文字类型

// This function you declared is expecting a SpecificItem type parameter
// and you are sending an object literal type parameter
const testTypeGuard = (item: SpecificItem) => {
    if (item.id === ItemIDs.ITEM_TYPE_1) {
        item.data.content = ''
    } else if (item.id === ItemIDs.ITEM_TYPE_2) {
        item.data.amount = 0;
    }
    return item;
}

因此,类型不匹配,这就是为什么出现错误的原因。 您需要做的就是按照@ przemyslaw-pietrzak的建议发送指定类型的对象,如下所示:

// Try to create item where ID can be any from ID enum
const breakTypeGuard = (id: ItemIDs, data: any) => {
    // Type 'ItemIDs' is not assignable to type 'ItemIDs.ITEM_TYPE_2'.
    // WHY
    testTypeGuard({ id, data } as SpecificItem);
}

答案 1 :(得分:1)

我认为您的代码没有错。 TS有时不评估类型(可能是针对性能问题)。

如果您想使此代码有效,我建议添加testTypeGuard({ id, data } as SpecificItem);。这不是很不安全,因为TS不允许映射所有类型。例如:

let fn = (arg: 42) => 42;
fn(1); // Argument of type '1' is not assignable to parameter of type '42'.
fn(1 as number) // Argument of type 'number' is not assignable to parameter of type '42'.