打字稿:类型'string'不能用于索引类型“ {{键:string]:任何;}”

时间:2020-01-03 02:30:24

标签: typescript

我正在制作一个向数组的每个元素添加附加属性的函数:

type AnyObj = { [key: string]: any };

function addIndexProp<T extends AnyObj>(
  obj: T[],
  myProp: string
): T[] {
  return obj.map(item => {
    item[myProp] = 'myProp';
    return item;
  });
}

但是我遇到了以下错误:

$ tsc --noEmit src/test.ts
src/test.ts:8:5 - error TS2536: Type 'string' cannot be used to index type 'T'.

8     item[myProp] = 'myProp';
      ~~~~~~~~~~~~


Found 1 error.

error Command failed with exit code 1

我不明白为什么会这样,因为我使用字符串索引类型指定了T,尤其是以下代码能很好地工作:

const test: { [key: string]: any } = { test: 1 };
const myProp = 'prop';
test[myProp] = 'myProp';

有人知道为什么第一个代码片段引发错误,以及如何解决该问题吗?谢谢!

1 个答案:

答案 0 :(得分:1)

T extends AnyObj的问题在于,T可以是值AnyObj可分配的任何东西,并且TypeScript将implicit index signatures赋予属性匹配的对象文字类型索引签名属性。因此,尽管没有索引签名,但可以将{a: number}之类的类型分配给AnyObj。因此,您可以编写以下代码而不会出现错误:

let obj = { a: 123, b: 345 };
const addedProps = addIndexProp([obj], "a");
addedProps[0].a.toFixed(); // okay at compile time, error at runtime

addIndexProp()内,有问题的行最终将值"myProp"分配给应该为number类型的属性。那就不好了。


正确的解决方案实际上取决于您的用例。如果您只是想让编译器满意而又没有使代码更安全,那么可以使用type assertion。即使item[myProp]="myProp"破坏了现有属性,这也可以让您分配myProp

function addIndexPropAssert<T extends AnyObj>(
  obj: T[],
  myProp: string
): T[] {
  return obj.map(item => {
    (item as any)[myProp] = 'myProp';
    return item;
  });
}

最安全的做法是不对原始obj元素进行突变,而使用更具表现力的类型来表示您正在添加或覆盖{ {1}}:

[myProp]

在这里,我们使用T,其中目标是一个新对象,因此不会修改任何现有对象。该类型最终将是function addIndexPropDoNotModify<T, K extends PropertyKey>(obj: T[], myProp: K) { return obj.map((item: Omit<T, K>) => expandType(Object.assign( { [myProp]: "myProp" } as Record<K, string>, item ))); } (意味着Object.assign()属性具有被添加回一个Omit<T, K>值)。

T只是一个帮助函数,它说服编译器将诸如[myProp]之类的映射类型的丑陋交集转换为诸如Record<K, string>之类的更简单的类型,如下所示:

[myProp]

这是它的工作方式:

string

所以这里的区别在于,尽管expandType()的类型为Omit<{a: number, b: number}, "a"> & Record<"a", string>,现在{a: string, b: number}的元素仍被视为类型function expandType<T>(x: T) { return x as any as T extends infer U ? { [K in keyof U]: T[K] } : never; }

无论如何,希望其中之一能给您一些指导。祝你好运!

Playground link to code