仅对对象的已定义成员强制执行类型,而不将 'keysof typeof [object]' 扩展为字符串 []?

Enforce a type only on defined members of an object, without widening the 'keysof typeof [object]' to string[]?

所以我创建了一个类型:

type Status = {
    name: string,
    description: string,
    statusLightColor: string
}

我有一个对象,我希望其成员的类型为 Status

const ListOfStatuses = {
    not_started: {name: "Not Started", description: "The work has not started.", statusLightColor: "Red"},
    in_progress: {name: "In Progress", description: "The task is currently in progress and will be complete soon.", statusLightColor: "Yellow"},
    is_finished: {name: "Is Finished", description: "The work is finished.", statusLightColor: "Green"},
}

最后我得到了另一种类型,我想 限制为 ListOfStatuses 的键。所以我这样做:

type StatusID = keyof typeof ListOfStatuses

神奇的是,它完全有效,因为 typescript 正在创建一个推理类型,其索引仅限于 ListOfStatuses 的键,而我的 IDE 显示 type StatusID = "not_started" | "in_progress" | "is_finished"

但是问题来了

为了让它工作,我不得不故意不定义 ListOfStatuses 的类型,以便 TypeScript 可以推断它。但是现在,如果我想向 ListOfStatuses 添加另一个 Status 并且我不小心,TS 不会对 ListOfStatuses.

的成员强制执行类型 Status

如果我这样定义类型:const ListOfStatuses : { [x: string]: Status } = {...},类型将被强制执行,但现在 StatusID 会中断并扩展为:type StatusID = string | number.

如何在不扩大其键类型的情况下对 ListOfStatuses 的成员强制执行类型 Status

我为什么要这样做:

类型 StatusID 的目的是创建对 Status 对象的轻量级引用,该对象包含更详细的信息,因为(在这种情况下)只会有 3 个有效状态,并且会有许多对象需要定义状态,这些状态将通过网络来回发送。

遵循减少重复的最佳实践,我想创建一个解决方案,让我或未来的开发人员只需编辑代码库中的单个对象即可添加或删除状态。

我考虑过为 ListOfStatuses 使用接口,但我不确定如何在代码库的其他部分轻松访问它的值。另外(我不确定这是否相关),我想避免使用函数调用来获取 Status 对象,因为我不确定它是否能与 Angular' s 与绑定一起使用时的变化检测。

这是我想出的一个解决方案,但它相当不优雅,因为您仍然需要编辑两个不同的东西,尽管如果 ListOfStatuses 和 [=22 至少它会抛出一个编译错误=] 不匹配。

type StatusID = 'not_started' | 'in_progress' | 'is_finished';
const ListOfStatuses: { [key in StatusID]: Status } = {...}

显然,在这个例子中更新两者并不是那么麻烦,但在更多对象的列表中可能会很烦人,我想为自己简化修改代码库细节的过程以及未来的其他开发者。

首先,我建议使用 [key: string]: Status,因为如果您稍后 add/remove 状态,它可能会破坏不相关的部分,这些部分依赖于硬编码属性而不是遍历成员。

尽管可以使用临时变量解决此问题,但可以使用内联函数将其隐藏。

type Status = {
    name: string,
    description: string,
    statusLightColor: string
}

const temp = {
  not_started: {
    // name: "Not Started",
    description: "The work has not started.",
    statusLightColor: "Red"
  },
  in_progress: { name: "In Progress", description: "The task is currently in progress and will be complete soon.", statusLightColor: "Yellow" },
  is_finished: { name: "Is Finished", description: "The work is finished.", statusLightColor: "Green" },
};

const ListOfStatuses: {
  [key in keyof typeof temp]: Status
} = temp; // This now throws an error that TS cannot convert from typeof temp to Status.