type Scales = 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl'
type TScale = { [k in Scales]: number }
type TSizing1 = { [k in Scales]?: string }
type TSizing2 = { [k in Scales]: string }
const scale: TScale = {
xxs: 1 / 8,
xs: 1 / 4,
s: 1 / 2,
m: 1,
l: 2,
xl: 4,
xxl: 8,
}
const sizing1: TSizing1 = {}
Object.entries(scale).forEach(([key, value]: [string, number]) => {
sizing1[key] = `${value * 1.6}rem`
})
const sizing2: TSizing2 = Object.assign(
{},
...Object.keys(scale).map(k => ({ [k]: `${scale[k] * 1.6}rem` })),
)
sizing1和sizing2都将出错,因为分别缺少TSizing1和TScale的索引签名。但是我应该如何向映射类型添加索引签名?
答案 0 :(得分:1)
错误之所以有意义,是因为您无法为具有特定键和且没有索引签名的接口分配string
密钥(索引签名是说明接口应该返回什么的指令未知键,例如string
键。
因此,您应该像这样更改代码:
Object.entries(scale).forEach(([key, value]: [Scales /* EDIT1 */, number]) => {
sizing1[key] = `${value * 1.6}rem`
})
const sizing2: TSizing2 = Object.assign(
{},
...Object.keys(scale).map((k) => ({ [k]: `${scale[k as Scales] /*EDIT2*/ * 1.6}rem` })),
)
我希望第一次编辑(EDIT1)很清楚。第二个(EDIT2)是必不可少的,因为Object.keys
不幸地仅返回string [],即使该参数具有已知的类型。
答案 1 :(得分:1)
问题不在于如何定义TSizing1
或TSizing2
,而是在调用Object.prototype.entries
或Object.prototype.keys
时如何处理对象类型。
解决方案
告诉TypeScript在对对象进行操作时使用缩小的特定类型。替换这些:
Object.entries(scale)
Object.keys(scale)
与那些:
(Object.entries(scale) as [Scales, number][])
(Object.keys(scale) as Scales[])
因此解决方案如下:
(Object.entries(scale) as [Scales, number][]).forEach(([key, value]) => {
sizing1[key] = `${value * 1.6}rem`;
});
const sizing2: TSizing2 = Object.assign(
{},
...(Object.keys(scale) as Scales[]).map(k => ({ [k]: `${scale[k] * 1.6}rem` })),
);
说明
在推断对象类型时,TypeScript非常糟糕。即使我们确切知道scale
的键是什么(它们非常明确地定义了Scales
),TypeScript也会将它们视为string
类型的某些数据。信息丢失。
要补偿这种损失,请断言它们的类型是您所知道的。