【问题标题】:Picking a type from an interface map based on map key根据映射键从接口映射中选择类型
【发布时间】:2021-02-03 10:28:31
【问题描述】:

我想编写一个通用的创建方法,该方法可以生成在地图中输入的不同对象。问题是打字稿将所有接口混合在一起,而不是只在接口映射中选择一个:

interface A  {
  a: string;
}

interface B {
  b: string;
}

interface ABMap {
  a: A;
  b: B;
}

function create<ID extends keyof ABMap>(id: ID): ABMap[ID] { // this is now combined A & B instead of A | B
  if (id === 'a') {
    return {a: 'a'}; // error a is missing b key 
  } else if (id === 'b') {
    return {b: 'b'};
  }
}

【问题讨论】:

  • 问题似乎是if语句没有缩小id的类型,它仍然在分支中有ID extends "a" | "b"类型。
  • 我目前已使用此帮助程序 type ValueOf&lt;T&gt; = T[keyof T]; 修复了错误,然后返回 ValueOf&lt;ABMap&gt;。但也许有更好的解决方案。
  • 这里是一个issue mentioning the problem,虽然ID &amp; 'a' 类型可能也没有帮助,但需要将其缩小为'a'

标签: typescript generics interface typescript-generics


【解决方案1】:

这里有两个问题。

首先,typescript 不会根据类型保护来缩小泛型的定义。这是一种已知和预期的行为,尽管它可能令人沮丧。当您检查if (id === 'a') 时,typescript 知道变量id 的值是A 类型。然而,它并没有将泛型缩小到仅A。泛型类型ID 是联合A | B 和变量idA 是完全有效的。所以当检查通过时,我们知道ID必须包含A,但我们不知道它是“A并且只有A”。

第二个问题是为什么ABMap[ID] 类型会缩小为A &amp; B 而不是A | B。那个我无法解释。

有(至少)三种解决方案。

  1. 你可以做你所做的,将返回类型扩大到ABMap[keyof ABMap] aka A | B

  2. 如果您正在处理一小组配对,例如本例中的两个,您可以通过函数重载来关联输入和输出:

function create(id: 'a'): ABMap['a'] 
function create(id: 'b'): ABMap['b'] 
function create(id: keyof ABMap): ABMap[keyof ABMap] {
  if (id === 'a') {
    return {a: 'a'};
  } else {
    return {b: 'b'};
  } 
}
  1. 您可以保留泛型并告诉 typescript 您返回的内容实际上是正确的类型,方法是使用 as 关键字。
function create<ID extends keyof ABMap>(id: ID): ABMap[ID] {
  if (id === 'a') {
    return {a: 'a'} as ABMap[ID];
  } else {
    return {b: 'b'} as ABMap[ID];
  }
}

Playground Link

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-23
    • 2018-02-02
    • 1970-01-01
    • 2023-02-01
    • 2023-03-28
    • 2020-06-08
    • 2017-08-08
    • 1970-01-01
    相关资源
    最近更新 更多