指定枚举类型

4

有一个枚举:

enum Foo {
    Bar, // should cause type error
    Baz = 'Baz'
}

是否可以指定一种类型,使其成为仅限字符串的枚举?

2个回答

2
如果您想要,可以执行以下操作:
创建一个函数,要求其输入仅具有字符串类型的属性:
const enforceStringEnum = <E extends Record<keyof E, string>>(e: E) => {};

然后,按照需要声明Foo

enum Foo {
  Bar, 
  Baz = 'Baz'
}

并使用Foo作为其输入调用该函数:

enforceStringEnum(Foo); // error!
// Type 'Foo.Bar' is not assignable to type 'string'.

这将会给你一个错误,关于 Foo.Bar,你可以回去修改来消除这个错误。
是的,你得到的错误并不是局限于 Foo 声明本身,但它确实允许你将 Foo 作为枚举而不是使用其他语言结构。是的,enforceStringEnum() 在运行时有(非常小的)影响。如果你想完全没有运行时副作用,也是可能的,但(在我看来)有点丑陋:
type EnforceStringEnum<E extends Record<keyof E, string>> = true;

enum Foo {
  Bar,
  Baz = 'Baz'
}

declare var fooWitness: EnforceStringEnum<typeof Foo>; // error! 
// Type 'Foo.Bar' is not assignable to type 'string'.

但它的工作方式是相同的。
编辑:或者,正如@estus提到的那样,您可以使用类型别名而不是变量声明:
type FooWitness = EnforceStringEnum<typeof Foo>; // error!
// Type 'Foo.Bar' is not assignable to type 'string'.

希望这有所帮助。祝你好运!

1
谢谢,最近有机会检查了这个答案。这是一个非常巧妙的技巧。我担心declare命名空间污染,我认为仅使用类型进行类型检查更好。如果您有更好的解决方法,请回答。 - Estus Flask
类型别名type alias也是一个不错的选择。无论是使用declare var还是type,都会向命名空间(值或类型)添加一个不必要的名称,所以你可以自行决定使用哪种方式。使用类型更加清晰,但你可以多次重新声明相同的var(只要类型相同),这样可能会减少命名空间的污染。 - jcalz

1
如果您检查枚举生成的JS代码,您会发现它等同于
enum Foo {
    Bar = 0, // should cause type error
    Baz = 'Baz'
}

但在TypeScript中可以创建这样的枚举类型,请参见异构枚举

您也可以使用命名空间:

namespace Foo {
    export const Bar: string = "Foo";
    export const Baz: string = "Baz";
}

或者一个带有静态字段的类:
class Foo {
    static Bar: string = "Foo";
    static Baz: string = "Baz";
}

第一个优点是在 Foo 中只有 BarBaz 属性,没有其他内容。

没错。我正在寻找一种方法,以防止开发人员(包括我自己)在需要异构枚举时意外创建混合枚举。 - Estus Flask
“意外创建混合枚举,而需要异构枚举”是什么意思? - Wickoo
意图是强制Foo成为仅限字符串的枚举。BarBar = 0应该导致类型错误。 - Estus Flask
我认为目前不可能,那么使用字符串字面量联合类型怎么样?例如 type Foo = "Bar" | "Baz"。这样你甚至不需要使用前缀 Foo,直接发送 "Bar""Baz" 即可。 - Wickoo
我记住了type Foo,但我的意图是摆脱裸字符串,因为Baz属性的值可能是很长的字符串。 - Estus Flask
你还有两个选项,很难在评论中表达清楚,我更新了答案。 - Wickoo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接