解决 TS7053: Element implicitly has an any type because expression of type string can't be used to index type

背景

有个接口

interface DataType {
    id: number;
    name: string;
    created_at: string;
    updated_at: string;
}

我的数据

{
    "id": 9,
    "created_at": "2024-03-11T17:50:16.129235+08:00",
    "updated_at": "2024-03-11T17:50:16.129293+08:00",
    "name": "qqqqqqqqqq1",
}

现在有个需求

需要把数据转换成

转换成这样

[
    {
        "span": 3,
        "key": "id",
        "label": "ID",
        "children": 9
    },
    {
        "span": 3,
        "key": "created_at",
        "label": "创建时间",
        "children": "2024-03-11T17:50:16.129235+08:00"
    },
    {
        "span": 3,
        "key": "updated_at",
        "label": "更新时间",
        "children": "2024-03-11T17:50:16.129293+08:00"
    },
    {
        "span": 3,
        "key": "name",
        "label": "名称",
        "children": "qqqqqqqqqq1"
    }
]

转换部分

const controlKey = {
    id: "ID",
    name: "名称",
    created_at: "创建时间",
    updated_at: "更新时间",
};
const items: DescriptionsProps["items"] = [];
for (const key in record) {
    const value: string = key.trim();
    const name = controlKey[value];
    items.push({
        span: 3,
        key: key,
        label: name,
        children: record[key],
    });
}

我以上面的方式来转换的,结果也确实转换成功了

但是,IDE工具会报错

const name = controlKey[value];

TS7053: Element implicitly has an any type because expression of type string can't be used to index type
{     
      id: string;     
      name: string;     
      created_at: string;
      updated_at: string; 
}
No index signature with a parameter of type string was found on type
{     
      id: string;     
      name: string;     
      created_at: string;     
      updated_at: string; 
}

children: record[key],

TS7053: Element implicitly has an any type because expression of type string can't be used to index type DataType
No index signature with a parameter of type string was found on type DataType

翻译下

元素隐式具有any类型,因为字符串类型的表达式不能用于索引类型DataType

意思就是说 controlKey 的类型定义与 record 的类型定义不匹配

controlKey 中的每个属性的值都是字符串类型,而 record 中的属性名可能是字符串类型也可能是数字类型,因此在使用 controlKey[value] 时 TypeScript 报错。

因为ts比较严格,所以这里就无法用 record 的key作为 controlKey 的key来索引

解决办法

给临时变量对象添加索引签名

const controlKey: { [key: string]: string } = {
    id: "ID",
    name: "名称",
    created_at: "创建时间",
    updated_at: "更新时间",
};

添加了索引签名 { [key: string]: string };,

允许使用字符串类型的键来访问 controlKey 对象的属性。

这样,就可以在 for-in 循环中使用 record 对象的属性名作为键来访问 controlKey 对象的对应属性值了。

使用明确推断的方式

const controlKey: Record<keyof DataType, string> = {
    id: "ID",
    name: "名称",
    description: "描述",
    url: "仓库URL",
    created_at: "创建时间",
    updated_at: "更新时间",
};
const items: DescriptionsProps["items"] = [];
for (const key in record) {
    const value: string = key.trim();
    const name = controlKey[value as keyof DataType];
    items.push({
        span: 3,
        key: key,
        label: name,
        children: record[key as keyof DataType],
    });
}

在这个解决方案中,我们使用 Record<keyof DataType, string> 来定义 controlKey

它确保了 controlKey 中的键是 DataType 中属性名的有效集合,并且值的类型为字符串。

然后在使用 controlKey 时,我们可以直接使用属性名作为索引键,并通过 as keyof DataType 来告诉 TypeScript,key 是 DataType 的有效属性名。

补充

泛型类型Record

这里简单介绍下泛型类型Record

该类型的源码

type Record<K extends keyof any, T> = {
    [P in K]: T;
};

它接收两个泛型参数:K和T。 K是一个可以 keyof any 的类型,代表键名的类型;T代表值的类型。

Record类型是一个对象类型,它的键是K类型,值是T类型。这个类型的对象的所有属性的值类型都是T。

例如

type Person = Record<string, number>;
const person: Person = {
    name: "aaa", // 错误,值类型不匹配
    age: 20
};

type Options = Record<"timeout" | "onComplete", number>;
const options: Options = {
    timeout: 1000,
    onComplete: 200
};

在上面的例子中,Person类型表示一个对象,其中任意属性的值都必须是number类型。

而Options类型表示一个对象,其中timeout和onComplete属性的值都必须是number类型。


而在咱们的代码中

Record<keyof DataType, string> 是用于创建一个新的对象类型,其中对象的键是来自另一个类型的属性名集合,而值都是指定的类型。

具体来说:

  • keyof DataType 表示取 DataType 类型的所有属性名构成的联合类型。
  • string 表示这个对象的值类型为字符串。
posted @ 2024-03-27 23:51  厚礼蝎  阅读(161)  评论(0编辑  收藏  举报