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

标签 typescript typescript-generics

我正在定义一个接口(interface),其中属性的类型之一取决于绑定(bind)到枚举的通用参数 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

最佳答案

我相信这里的问题与 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参数,它将最终成为相同的具体类型。


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

关于typescript - 'CustomEnum.Case' 可分配给类型 'T' 的约束,但 'T' 可以使用约束 'CustomEnum' 的不同子类型进行实例化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57363278/

相关文章:

typescript - 如何将元组类型转换为联合?

typescript - 如何创建严格的 Exclude 实用程序类型?

typescript - 使用 Getter 和 Setter 计算的 Vue 属性不会在 Webpack 中编译

typescript - 返回类型化索引的通用数组

typescript - getter 和 setter 具有不同类型的索引签名

javascript - TypeScript - 如何从事件处理程序方法访问类实例

typescript - 在受约束的通用函数中混淆 "[ts] Type ... is not assignable to type [2322]"错误

typescript - 为什么在泛型类型上添加约束会改变推理行为?

javascript - Eslint 警告不允许我推送到仓库

javascript - 如何将缺失值添加到两个关联数组? (JavaScript)