背景
有个接口
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
表示这个对象的值类型为字符串。