“ CustomEnum.Case”可分配给类型“ T”的约束,但是可以使用约束“ CustomEnum”的另一子类型实例化“ T”

时间:2019-08-05 17:17:11

标签: typescript typescript-generics

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

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

1 个答案:

答案 0 :(得分:1)

我认为这里的问题与this issue中描述的问题相同……您希望编译器将{[K in Scopes]: ScopedData<K>}[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<T>[],而不是将其返回为Data[T]["items"]。这些事实将是同一回事,当您实际上对具体类型的getItemType()参数进行调用 scope时,它将最终变为相同的具体类型。< / p>


或者您可以承认您的推理能力优于编译器,并使用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