Typescript中映射类型的索引签名

Typescript中映射类型的索引签名,typescript,Typescript,sizing1和sizing2都将出错,因为TSizing1和TScale上分别缺少索引签名。但是我应该如何向映射类型添加索引签名呢?这些错误是有意义的,因为您不能将字符串键分配给具有特定键的接口和没有索引签名的接口(索引签名是一条说明接口应该为未知键返回什么的指令,即字符串键) 因此,您应该如下更改代码: type Scales = 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl' type TScale = { [k in Scales]: numb

sizing1和sizing2都将出错,因为TSizing1和TScale上分别缺少索引签名。但是我应该如何向映射类型添加索引签名呢?

这些错误是有意义的,因为您不能将
字符串
键分配给具有特定键的接口和没有索引签名的接口(索引签名是一条说明接口应该为未知键返回什么的指令,即
字符串
键)

因此,您应该如下更改代码:

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` })),
)

我希望第一次编辑(EDIT1)是清楚的。第二个(EDIT2)是一个必要的恶魔,因为
对象。即使参数的类型已知,keys也只能返回字符串[]。

问题不在于如何定义
TSizing1
TSizing2
,但是调用
object.prototype.entries
object.prototype.keys
时如何处理对象类型

解决方案

告诉TypeScript在操作对象时使用狭窄的特定类型。替换这些:

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` })),
)
其中:

Object.entries(scale)
Object.keys(scale)
因此,解决方案如下所示:

(Object.entries(scale) as [Scales, number][])
(Object.keys(scale) as Scales[])
解释

TypeScript在推断对象类型时非常糟糕。即使我们确切地知道
scale
的键是什么(它们的定义非常清楚
Scales
),TypeScript也会将它们视为
string
类型的一些数据。信息丢失了


为了弥补这一损失,请将它们的类型声明为您所知道的类型。

as
语法有区别吗?有区别,但在您的情况下,两者都会起作用。前者表示类型断言,后者是类型定义。一个诱使编译器思考与通常不同的问题,而另一个则告诉编译器您的意图。我选择尽早使用断言,因为如果将
map
更改为任何其他方法(如
reduce
),它仍然可以工作,而将内联定义添加到
forEach
只能在其回调内工作。
(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` })),
);