如何将对象键的类型限制为命名空间常量?

How to constrain type of object keys to namespace constants?

我想定义一个映射类型,其键是命名空间下所有常量的值。

我找不到涵盖此问题的其他问题。 This question and its duplicate talk about JavaScript, whereas I'm looking to strongly type the property of a class. I also couldn't find a reference in the Typescript handbook.

描述:

我有这样的命名空间:

export namespace Controls {
    export const Foo = "foo";
    export const Bar = "bar";
    export const Baz = "baz";
    // ... a dozen others
}

此命名空间不包含任何其他内容。仅导出 consts.

我想定义一个对象类型来表达以下含义:“此对象键只能是在该命名空间中声明的常量值”。天真地像:

type Namespaced = { [K in Controls]?: ControlDelegate }

以上无法编译,因为我不能将名称空间用作类型。出于同样的原因,索引类型也不起作用:

type NamespaceKeys = Controls[keyof typeof Controls]

然后我有了这个顿悟可以用:

{ [K in keyof typeof Controls]?: ControlDelegate }

确实可以编译,并且解析后的类型看起来像我想要的,但是我无法实例化文字:

this.controlDelegates = {
            [Controls.Foo]: new FooControlDelegate() // it implements ControlDelegate
        }

编译错误如下:

Type '{ "foo": FooControlDelegate; }' is not assignable to type '{ readonly Foo?: ControlDelegate; ... Object literal may only specify known properties

将对象键的类型限制为特定命名空间下的值的正确方法是什么?

keyof typeof Controls 为您提供 Controls,它们是“Foo”、“Bar”、“Baz”。您想要的是 Controlsvalues,即“foo”、“bar”、“baz”(小写)。

您可以通过 typeof Controls[keyof typeof Controls] 实现。

感谢@cdimitroulas 的建议,我最终将 属性 声明为:

controlDelegates: { [K in typeof Controls[keyof typeof Controls]]?: ControlsDelegate }

然后可以正确初始化为:

     this.controlDelegates = {
         [Controls.Foo]: new FooControlDelegate()
     }

但是,这种方法无法扩展。下一次将某物添加到命名空间时,如果该某物具有不可分配给对象键的类型——例如a class — 打字会中断。

由于命名空间只包含字符串常量,一个更具前瞻性的解决方案是将其转换为 enum:

export enum Controls {
    FOO = "foo";
    BAR = "bar";
    BAZ = "baz";
    // ... and so on
}