'CustomEnum.Case' 可分配给类型 'T' 的约束,但 'T' 可以用约束的不同子类型 'CustomEnum' 实例化

'CustomEnum.Case' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'CustomEnum'

我正在定义一个接口,其中 属性 的类型之一取决于绑定到枚举的通用参数 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<T> 作为以下函数的 return 类型:

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

我相信这里的问题与 this issue... you want the compiler to evaluate {[K in Scopes]: ScopedData<K>}[P] to something like ScopedData[P], where P is a generic type parameter that extends K. But the compiler doesn't do this sort of higher-order reasoning where a generic function of a concrete type is simiplified before the generic type is resolved; there has been a suggestion 中描述的问题相同,在某些情况下会发生这种情况,但从 TS3.5 开始就不存在了。

因此,变通办法... 编译器可以验证以下内容:

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

而不是 return 将 data[scope].items 的类型设置为 ScopedItem<T>[],return 将其设置为 Data[T]["items"]。那些将变成同一件事,当您实际 调用 getItemType() 一个具体类型的 scope 参数时,它最终将成为相同的具体类型。


或者你可以承认自己的推理能力比编译器高明,然后用一个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