首页 > 解决方案 > 从索引联合类型推断原始类型

问题描述

有没有办法从作为索引类型创建的类型联合中推断出原始类型?这是一个例子。

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上面的代码片段之外,我还添加了一个Idand CodeTemplate,其中两者都由HandlerFromType<Type>. 添加Id一切正常后,但是当我添加 时CodeTemplate,突然类型检查失败,好像HandlerFromType<Type>推断为所有处理程序一样。

编辑2:

我似乎找到了问题的根源。这是ts playground上最小示例的链接。由于它的模板HandlerFromType有一个默认值,当它在里面使用时,使用这个默认值。然后在声明中,错误不可分配给.UCodeTemplatecodeTemplateFooHandlerGooHandler

标签: typescripttypes

解决方案


您可以使用该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; }

推荐阅读