【问题标题】:Generic data by object key按对象键的通用数据
【发布时间】:2021-05-14 02:28:49
【问题描述】:

这是我的代码:

type Bucket = {
  [key:string]:Map<string,{data:any}>
}

现在我要做的是创建一个type,它可以让我通过密钥获得custom 数据。正如您在下面的代码中看到的,我想为每个键强烈键入 data 对象。我已经尝试在 Bucket 类型上使用通用对象,但这会将所有键锁定到相同的数据。

有没有办法为每个键设置不同的强类型 data

const bucket:Bucket = {
  a:new Map(),
  b:new Map<string,{data:boolean}>()
}

bucket.a.set('a',{data:1})
bucket.b.set('b',{data:'b'}) // this should error

const getA = bucket.a.get('a') // data is any - should be inferred to number?
const getB = bucket.b.get('b') // data is any - should be boolean

TS Playground

【问题讨论】:

标签: typescript mapped-types


【解决方案1】:

当您使用索引签名 ({ [x: &lt;key type&gt;] : &lt;value type&gt; }) 定义 Bucket 类型时,您告诉编译器特定元素与其类型之间没有直接关联。此外,any 不是“占位符”类型,它是类型检查的opts out。因此,编译器会向您显示您告诉它的内容:

  1. bucket 的所有键都是string 类型
  2. bucket 具有 Map&lt;string, { data: any } 类型的值。

然后您将一个对象分配给bucket 变量。此时,将检查该值与提供的类型 (Bucket) 的 兼容性,并且由于您禁用了对 data 成员的类型检查,anything 正常,因此没有错误。

接下来,当您将get 的值设为bucket 的成员时,类型被推断为{ data: any; } | undefined,因为不能保证"a""b" 将出现在[x: string] 中,因此值可以是undefined。我们已经讨论了为什么data 没有缩小范围。

如果您从bucket 中删除类型注释,您会注意到推理大大改进,因为现在也可以推断类型:{ a: Map&lt;any, any&gt;; b: Map&lt;string, { data: boolean; }&gt;。同时,您会丢失类型检查 bucket 的成员可以包含的内容。

那么,你能做些什么呢?您需要明确告诉编译器某些键和值之间的相关性是什么。使您的Bucket 通用可以帮助我们实现这一目标:

type Bucket<T> = {
  [ P in keyof T ]: Map<string, { data: T[P] }>
}

现在您已经正确推断出data 类型、关于bucket 形状的保证以及以更冗长为代价的自动完成,这是为这些好处付出的小代价(此外,您可以将类型参数设为接口或类型):

type Bucket<T> = {
  [ P in keyof T ]: Map<string, { data: T[P] }>
}

const bucket: Bucket<{ a: number, b: boolean, c?: string }> = {
  a:new Map(),
  b:new Map(),
};

bucket.a.set('a', { data: 1 })
bucket.b.set('b', { data: 'b' }) // Type 'string' is not assignable to type 'boolean'

const getA = bucket.a.get('a') // data is number
const getB = bucket.b.get('b') // data is boolean

Playground


Aleksey L. 提到的标识函数做同样的事情——从bucket 中删除显式类型注释,同时保留类型检查(extends Bucket 约束)并返回文字类型(&lt;T&gt;(a:T) =&gt; T 形式)。它有在编译代码中发出函数的成本,但它允许您推断 bucket 类型。

【讨论】:

  • @IvanV。 NP - 具有泛型类型参数的映射类型是我需要关联键和值时首先想到的(尽管我也喜欢使用标识函数的想法)
猜你喜欢
  • 2021-11-19
  • 1970-01-01
  • 2021-11-10
  • 2015-11-28
  • 2015-10-14
  • 1970-01-01
  • 2020-07-11
  • 2022-11-28
  • 1970-01-01
相关资源
最近更新 更多