TypeScript泛型在Record中不起作用?

huangapple go评论62阅读模式
英文:

TypeScript generics are not working for Record?

问题

我有这个 keyBy 函数:

export function keyBy<Key extends string | number, T>(items: T[] | undefined, extractId: (item: T) => Key): Record<Key, T> {
  const obj: Record<Key, T> = {};

  if (items) {
    items.forEach(item => {
      const id = extractId(item);
      obj[id] = item;
    });
  }

  return obj;
}

它可以用来将对象数组转换为这些对象的字典,按您想要的方式进行键控。例如:

const users = [user1, user2, ...];
const usersById = keyBy(users, u => u.id);

现在我想要更改此函数,使其不仅适用于数字键(如 id),还适用于字符串键:

export function keyBy<Key extends string | number, T>(items: T[] | undefined, extractId: (item: T) => Key): Record<Key, T> {
  const obj: Record<Key, T> = {};

  if (items) {
    items.forEach(item => {
      const id = extractId(item);
      obj[id] = item;
    });
  }

  return obj;
}

这会引发错误 Type '{}' is not assignable to type 'Record<Key, T>'. ts(2322)。为什么不起作用?

我认为问题在于现在每个 id 都可以是字符串或数字,而我想要的是所有的 id 都是字符串或数字,但当然它们都是相同的类型。

英文:

I have this keyBy function:

export function keyBy&lt;T&gt;(items: T[] | undefined, extractId: (item: T) =&gt; number): Record&lt;number, T&gt; {
  const obj: Record&lt;number, T&gt; = {};

  if (items) {
    items.forEach(item =&gt; {
      const id = extractId(item);
      obj[id] = item;
    });
  }

  return obj;
}

It can be used to turn an array of objects into a dictionary of these objects, keyed by whatever you want. For example:

const users = [user1, user2, ...]l
const usersById = keyBy(users, u =&gt; u.id);

Now I want to change this function to not only work with numeric keys like ids, but also with strings:

export function keyBy&lt;Key extends string | number, T&gt;(items: T[] | undefined, extractId: (item: T) =&gt; Key): Record&lt;Key, T&gt; {
  const obj: Record&lt;Key, T&gt; = {};

  if (items) {
    items.forEach(item =&gt; {
      const id = extractId(item);
      obj[id] = item;
    });
  }

  return obj;
}

This gives the error Type &#39;{}&#39; is not assignable to type &#39;Record&lt;Key, T&gt;&#39;. ts(2322). Why is this not working?

I think the problem is that now every id can be either a string or a number, whereas what I want is that all ids are either a string or a number, but of course they are all the same type.

答案1

得分: 1

你不能将一个空对象分配给具有可枚举键的Record
> 注意:我不确定它是否被称为“可枚举”。
> 然而,在这个帖子中,“可枚举”意味着所有的值都是已知的,比如"a" | "b"1 | 2 | 3,并且stringnumber不是“可枚举”。

const obj1: Record<"id", number> = {}
// 类型“{}”中缺少属性“'id'”,但在类型“Record<"id", number>”中是必需的。

const obj2: Record<string, number> = {}
// OK

在你的keyBy函数中,可以将可枚举值传递给Key泛型参数,这会导致空对象分配问题。

最简单的方法就是使用as关键字将它转换成你想要的类型:

const obj = {} as Record<Key, T>;
英文:

You can not assign an empty object to a Record with enumerable keys:
> Note: I am not sure it is called "enumerable".
> However, in this post, "enumerable" means all value are known like &quot;a&quot; | &quot;b&quot; or 1 | 2 | 3, and string or number is not "enumerable"

const obj1: Record&lt;&quot;id&quot;, number&gt; = {}
// Property &#39;id&#39; is missing in type &#39;{}&#39; but required in type &#39;Record&lt;&quot;id&quot;, number&gt;&#39;.

const obj2: Record&lt;string, number&gt; = {}
// OK

In your keyBy function, it is possible to pass enumerable into Key generic parameter, and that cause the empty object assignment issue.

The most simple way is just use as keyword to convert it to what you want:

const obj = {} as Record&lt;Key, T&gt;;

huangapple
  • 本文由 发表于 2023年5月28日 17:19:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76350778.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定