首页 > 解决方案 > 从数组的泛型推断正确类型的问题

问题描述

在以下示例中,我很难弄清楚如何缩小推断类型。目前,所有格式函数都接受string | number | boolean并期望返回string | number | boolean.

理想情况下,我想根据typeID.

enum TypeID {
  Number = "__number__",
  String = "__string__",
  Boolean = "__boolean__"
}

type Type<TYPE_ID extends TypeID> = {
  [TypeID.Number]: number;
  [TypeID.String]: string;
  [TypeID.Boolean]: boolean;
}[TYPE_ID];

type Item<
  TYPE_ID extends TypeID,
  TYPE extends Type<TYPE_ID> = Type<TYPE_ID>
> = {
  typeID: TypeID;
  format: (input: TYPE) => TYPE;
};

type Options<TYPE_ID extends TypeID> = Array<Item<TYPE_ID>>;

const someFunc = <TYPE_ID extends TypeID>(options: Options<TYPE_ID>) => {
  return null as any;
};

someFunc([
  {
    typeID: TypeID.Number,
    format: input => input // these should have type "number"
  },
  {
    typeID: TypeID.Boolean,
    format: input => input // these should have type "boolean"
  },
  {
    typeID: TypeID.String,
    format: input => input // these should have type "string"
  }
]);

标签: typescriptgenericstype-safety

解决方案


TYPE拥有如此相似的类型名称( vs Typevs TYPE_IDvs )非常令人困惑,TypeID所以我将在接下来的内容中使名称不同......对于泛型参数,我使用一两个大写字母(无论出于何种原因,这是惯例)。这是Type

type Type<T extends TypeID> = {
  [TypeID.Number]: number;
  [TypeID.String]: string;
  [TypeID.Boolean]: boolean;
}[T];

这是Item

type Item<T extends TypeID> = {
  typeID: T; // <-- T, not TypeID
  format: (input: Type<T>) => Type<T>;
};

请注意,这是从您的示例中进一步修改的。您在这里有两个类型参数,但似乎只为第二个使用了默认值,所以我删除了它并使用了该默认值。但这里重要的修改是typeID属性是泛型类型T,而不是具体类型TypeID。我认为您可能打算在您的代码中执行此操作,但也许很难看出区别。


现在我要从这里开始的方法是生成一个名为的类型SomeItem,它是所有可能Item<T>类型的联合。这种具体类型比泛型类型更容易使用:

type _SomeItem<T extends TypeID> = T extends any ? Item<T> : never;
type SomeItem = _SomeItem<TypeID>;
// type SomeItem = Item<TypeID.Number> | Item<TypeID.String> | Item<TypeID.Boolean>

这通过在 , 的定义中使用分布条件类型_SomeItem来分布Item<T>在值的联合中TypeID

最后,someFunc可以是一个具体的函数:

const someFunc = (options: Array<SomeItem>) => {
  return null as any;
};

当你使用它时,你会得到你期望的推断:

someFunc([
  {
    typeID: TypeID.Number,
    format: input => input // has type number
  },
  {
    typeID: TypeID.Boolean,
    format: input => input // has type boolean
  },
  {
    typeID: TypeID.String,
    format: input => input // has type string
  }
]);

好的,希望有帮助。祝你好运!
链接到代码


推荐阅读