根据映射键从接口映射中选择类型

Posted

技术标签:

【中文标题】根据映射键从接口映射中选择类型【英文标题】: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' 另外,问题有点像***.com/questions/60204107/… 【参考方案1】:

这里有两个问题。

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

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

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

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

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

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';
   

    您可以保留泛型并告诉 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

【讨论】:

以上是关于根据映射键从接口映射中选择类型的主要内容,如果未能解决你的问题,请参考以下文章

SQLAlchemy 不仅通过主键从身份映射中获取项目

Underscore.js:使用在对象中找到的键从对象列表中创建映射

使用某些键从java.properties文件加载映射

在 django 映射中如何获得完整的选择字段计数

Sonata Admin => 只选择给定的鉴别器映射类型

映射数组时如何设置选择选项的默认值