TypeScript:“类型是通用的,只能为阅读建立索引。(2862)”

问题描述 投票:0回答:1

想象一下我想为其添加类型的示例 JS 函数:

const remap = (obj) => {
  const mapped = {};
  Object.keys(obj).forEach((key) => {
    mapped[key] = !!key;
  });

  return mapped;
};

我正在尝试通过泛型(在这个 TS 游乐场中)添加类型,但我一直遇到此错误:

Type 'Mapped<T>' is generic and can only be indexed for reading.(2862)
type Mapped<T> = {
  [K in keyof T]?: boolean;
};

const remap = <T extends Record<string, unknown>>(
  obj: T
) => {
  const mapped: Mapped<T> = {};
  Object.keys(obj).forEach((key) => {
    mapped[key] = !!key; // Type 'Mapped<T>' is generic and can only be indexed for reading.(2862)
  });

  return mapped;
};

我想了解为什么 TS 不允许我写入这个泛型类型的对象,以及是否有其他方法可以解决它。我希望 TS 能够理解

mapped
的类型并允许我写入它,但它不会。

只有让TS接受这个才能在返回时使用

as
吗?

const remapWithAs = <T extends Record<string, unknown>>(
  obj: T
) => {
  const mapped: Record<string, boolean> = {};
  Object.keys(obj).forEach((key) => {
    mapped[key] = !!key;
  });

  return mapped as Mapped<T>; // Is this my only option?
};
typescript typescript-generics
1个回答
0
投票

错误的根本原因是

Object.keys(x)
在TS库中声明为返回string[]
而不是类似
(keyof typeof x)[]
之类的东西。这是故意的;请参阅
为什么 Object.keys 在 TypeScript 中不返回 keyof 类型?。因此,当您索引 mapped[key]
 时,您使用的是 
string
 键,而不一定是 
Mapped<T>
 键。因此,从技术上讲,向其写入 
boolean
 是不安全的,因为也许您正在写入 
Mapped<T>
 不知道的密钥,并且您无法确定 
that 键会接受 boolean
。您需要使 
Mapped<T>
 只是 
{[k: string]: boolean}
(这意味着 
T
 是不必要的),或者您需要 
断言 您正在做的事情是安全的。

请注意,TypeScript 会让您从

boolean 读取

 
mapped[key]
,尽管这在技术上也是不安全的:

Object.keys(obj).forEach((key) => { const test = mapped[key]; // boolean | undefined });
TypeScript 就是这样。无论如何,这就是为什么您收到错误消息:

Mapped<T>

只能被索引(使用
string
)以供阅读。它过去只是说你根本无法用 
Mapped<T>
 索引 
string
 ,但由于这显然是不正确的,如上所示,他们将错误消息更改为新的措辞。请参阅 
microsoft/TypeScript#47357 了解更多信息。


无论如何,断言的正常方法是说你确信

Object.keys(obj)

 将返回 
(keyof T)[]
,尽管 TypeScript 保留可能会出现其他键。如果你这样做:

const remap = <T extends Record<string, unknown>>( initialState: T ) => { const mapped: Mapped<T> = {}; (Object.keys(initialState) as (keyof T)[]).forEach(key => { mapped[key] = !!key; // okay }); return mapped; };
然后它就按照写的那样工作了。 TypeScript 很乐意允许 

mapped[key]

 属于 
Mapped<T>[keyof Mapped<T>]
 类型,即 
boolean | undefined
,因此它会接受 
boolean

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.