【问题标题】:'CustomEnum.Case' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'CustomEnum'“CustomEnum.Case”可分配给“T”类型的约束,但“T”可以用约束“CustomEnum”的不同子类型实例化
【发布时间】:2019-12-13 06:05:26
【问题描述】:

我正在定义一个接口,其中一种属性类型取决于绑定到枚举的通用参数 P。我正在使用以下方法:

export enum Scopes {
  Fruit = 'fruit',
  Vegetables = 'vegetables',
}

export enum FruitItemTypes {
  Strawberry = 'strawberry',
  Rasberry = 'rasberry'
}

export enum VegetableItemTypes {
  Potatoes = 'potatoes',
  Carrots = 'currency',
}


export type ItemTypes = FruitItemTypes | VegetableItemTypes

interface ItemTypeForScope {
  [Scopes.Fruit]: FruitItemTypes;
  [Scopes.Vegetables]: VegetableItemTypes;
}

export interface Item {
  id: string;
  type: ItemTypes;
}
export interface ScopedItem<T extends Scopes> extends Item {
  type: ItemTypeForScope[T];
}
export interface ScopedData<T extends Scopes> {
  items: ScopedItem<T>[];
}

export type Data = { [scope in Scopes]: ScopedData<scope> };

我也想用ScopedItem&lt;T&gt;作为下面函数的返回类型:

const getItemType = <T extends Scopes>(data: Data, scope: T): ScopedItem<T>[] => {
    return data[scope].items 
}

但是我收到以下错误,但根据我的说法,通用参数 T 最终将成为枚举案例之一。

Type 'ScopedItem<Scopes.Fruit>[] | ScopedItem<Scopes.Vegetables>[]' is not assignable to type 'ScopedItem<T>[]'.
  Type 'ScopedItem<Scopes.Fruit>[]' is not assignable to type 'ScopedItem<T>[]'.
    Type 'ScopedItem<Scopes.Fruit>' is not assignable to type 'ScopedItem<T>'.
      Type 'Scopes.Fruit' is not assignable to type 'T'.
        'Scopes.Fruit' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Scopes'.

playground

【问题讨论】:

  • 关于你的游乐场链接......那些不是联合和交叉点。您使用的 logical operators 非常不同。 x || yx &amp;&amp; y 将始终评估为 xy 之一,具体取决于 x 的真实性/虚假性。
  • 感谢详细解答和问题参考,相信Data[T]["items"]和编译器推断的类型是一样的。在这种情况下,类型断言似乎是一个很好的解决方案。关于逻辑运算符,这是漫长的一天,但感谢有关此示例中无关紧要的反馈:)
  • this answer 非常详细地介绍了此错误消息。看看吧。

标签: typescript typescript-generics


【解决方案1】:

我相信这里的问题与this issue 中描述的问题相同...您希望编译器将{[K in Scopes]: ScopedData&lt;K&gt;}[P] 评估为ScopedData[P],其中P 是扩展K 的泛型类型参数.但是编译器不会进行这种高阶推理,即简化具体类型的泛型函数解析泛型类型之前;在某些情况下,有一个 suggestion 来实现这一点,但在 TS3.5 中不存在。

因此,解决方法... 编译器可以验证以下内容:

const getItemType = <T extends Scopes>(
  data: Data,
  scope: T
): Data[T]["items"] => {
  return data[scope].items;
};

不要将data[scope].items 的类型返回为ScopedItem&lt;T&gt;[],而是将其返回为Data[T]["items"]。结果是一样的,当你在具体类型的scope 参数上实际调用 getItemType() 时,它最终会成为相同的具体类型。


或者你可以承认你的推理能力比编译器高,并使用type assertion让编译器知道谁是老大:

const getItemTypeAssertion = <T extends Scopes>(
  data: Data,
  scope: T
): ScopedItem<T>[] => {
  return (data[scope] as ScopedData<T>).items; // I am smarter than the compiler ?
};

希望其中之一对您有用。祝你好运!

Link to code

【讨论】:

    猜你喜欢
    • 2021-12-10
    • 2020-11-25
    • 1970-01-01
    • 2021-06-19
    • 2023-01-11
    • 2022-01-20
    • 2021-02-13
    • 2021-10-14
    • 2021-05-22
    相关资源
    最近更新 更多