【问题标题】:Index signature for a mapped type in TypescriptTypescript 中映射类型的索引签名
【发布时间】:2023-03-13 01:24:01
【问题描述】:
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 上分别缺少索引签名。但是我应该如何将索引签名添加到映射类型?

【问题讨论】:

    标签: typescript


    【解决方案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[] 即使参数已经知道类型。

    【讨论】:

      【解决方案2】:

      问题不在于TSizing1TSizing2 是如何定义的,而是在调用Object.prototype.entriesObject.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 类型的一些数据。信息丢失。

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

      【讨论】:

      • as: 的语法有区别吗?
      • 有,但在你的情况下,两者都可以。前者表示一个类型断言,后者是一个类型定义。一种欺骗编译器使其思考与通常情况不同,而另一种则告诉编译器您的意图是什么。我选择尽早使用断言,因为如果您将map 更改为reduce 之类的任何其他方法,它仍然可以工作,而向forEach 添加内联定义只能在其回调中工作。
      猜你喜欢
      • 2021-03-06
      • 2018-05-07
      • 2018-12-30
      • 2020-02-02
      • 1970-01-01
      • 2019-05-19
      • 2020-08-07
      • 2020-12-24
      • 2020-02-21
      相关资源
      最近更新 更多