从索引并集类型推断出原始类型

时间:2019-06-10 10:59:02

标签: typescript types

是否有一种方法可以从创建为索引类型的类型联合中推断出原始类型?这是一个例子。

class FooHandler { type: "Foo" = "Foo"; }
class GooHandler { type: "Goo" = "Goo"; }
class UniHandler { type: "A" | "B" | "C" = "A"; }

type HandlerVariant = FooHandler | GooHandler | UniHandler;
type HandlerTypeVariant = HandlerVariant["type"];

// Infer the concrete `HadnlerVariant`
type HandlerFromType<Type extends HandlerTypeVariant> = ??;

最终,我想做的是创建一个映射类型,该类型每个type都包含一个该类型处理程序的实例。

type HandlerRegistryType = {
    [Type in HandlerTypeVariant]: HandlerFromType<Type>;
}

编辑:

在这种更为复杂的情况下,似乎接受的解决方案在派生类型方面存在一些问题。 -链接到ts游乐场-。

除了上面的代码片段中的type之外,我还添加了IdCodeTemplate,它们都由HandlerFromType<Type>进行了专门处理。添加Id后,一切正常,但是当我添加CodeTemplate时,类型检查突然失败,就好像推断HandlerFromType<Type>是所有处理程序一样。

编辑2:

我似乎已经找到问题的根源。这是ts playground上最小示例的链接。由于HandlerFromType的{​​{1}}模板具有默认值,因此在U内使用该模板时,将使用该默认值。然后在CodeTemplate声明中,错误codeTemplate无法分配给FooHandler

1 个答案:

答案 0 :(得分:1)

您可以使用Extract类型来获取扩展特定类型的并集的成员。它将按您期望的那样工作:

class FooHandler { type: "Foo" = "Foo"; }
class GooHandler { type: "Goo" = "Goo"; }

type HandlerVariant = FooHandler | GooHandler;
type HandlerTypeVariant = HandlerVariant["type"];

type HandlerFromType<Type extends HandlerTypeVariant> = Extract<HandlerVariant, { type: Type }>;

type HandlerRegistryType = {
    [Type in HandlerTypeVariant]: HandlerFromType<Type>;
}

修改

如果type是其中一个成员的并集,则可以很好地使用该版本,但是会更加复杂,并且需要分布式条件类型:

class FooHandler { type: "Foo" = "Foo"; foo: string }
class GooHandler { type: "Goo" = "Goo"; goo: string}
class UniHandler { type: "A" | "B" | "C" = "A"; uni: string}

type HandlerVariant = FooHandler | GooHandler | UniHandler;
type HandlerTypeVariant = HandlerVariant["type"];

type HandlerFromType<T, U extends { type: HandlerTypeVariant } = HandlerVariant> =  
  U extends U ? // Distribute over U, from here on U will be each meber of the union in turn 
  T extends U["type"] ? U : never // if the single elemnt T extends whatever union U has at type then we take U otherwise we remove it 
  : never; 


type HandlerRegistryType = {
  [Type in HandlerTypeVariant]: HandlerFromType<Type>;
}
// same as  { Foo: FooHandler; Goo: GooHandler; A: UniHandler; B: UniHandler; C: UniHandler; }