【问题标题】:Difference between string enums and string literal types in TSTS 中字符串枚举和字符串字面量类型的区别
【发布时间】:2018-09-20 13:34:25
【问题描述】:

假设我想确保{ myKey: '' } 中的myKey 只包含字符串foobarbaz,我可以通过两种方式实现。

   // with a String Literal Type 
   type MyKeyType = 'foo' | 'bar' | 'baz';

    // or with a String Enum   
    enum MyKeyType {
       FOO = 'foo',
       BAR = 'bar',
       BAZ = 'baz'
    }

我想知道一个相对于另一个的优缺点在哪里,因为在我看来两者都是一样的(除了我访问值的方式,例如条件检查)。

我在 TS 文档中发现的唯一区别是枚举是运行时的真实对象,在某些情况下可能需要这样做。

【问题讨论】:

  • 到目前为止,我还没有发现enum 比字符串文字类型工作得更好、更清晰或更安全的案例。字符串文字的一个优点是您可以利用 pick/keyof 的泛型。我不认为你可以用 enum 做到这一点。
  • 要考虑的一件事是可维护性:如果字符串的值可能会发生变化,那么使用字符串枚举意味着只更改 1 个字符串文字,而使用字符串类型意味着在任何地方更改它们'重复使用。

标签: typescript


【解决方案1】:

要理解的关键是字符串枚举的值是不透明的

字符串枚举的预期用例是您不希望其他代码知道或关心支持MyKeyType.FOO 的文字字符串是什么。这意味着您将无法将 文字字符串 "bar" 传递给接受 MyKeyType 的函数——您必须改为编写 MyKeyType.BAR

【讨论】:

  • 所以你会说它们的行为方式没有真正的区别(它们都实现了相同的目标),但是 Enums 提供了一些额外的抽象,可能在项目的后期阶段很有帮助,例如重构?
  • @jowey 如果你正在构建一个组件库,例如你想公开我认为的文字字符串。
【解决方案2】:

嗯,转译代码中的字符串枚举和文字类型之间存在区别

比较 Typescript 代码

// with a String Literal Type 
type MyKeyType1 = 'foo' | 'bar' | 'baz';

// or with a String Enum   
enum MyKeyType2 {
   FOO = 'foo',
   BAR = 'bar',
   BAZ = 'baz'
}

使用转译的 JavaScript 代码

// or with a String Enum   
var MyKeyType2;
(function (MyKeyType2) {
    MyKeyType2["FOO"] = "foo";
    MyKeyType2["BAR"] = "bar";
    MyKeyType2["BAZ"] = "baz";
})(MyKeyType2 || (MyKeyType2 = {}));

您可以看到,没有为字符串文字生成代码。因为 Typescripts Transpiler 仅在转译时用于类型安全。在运行时,字符串文字是“生成为哑”的字符串。文字的定义和用法之间没有引用。

所以还有第三种选择,称为 const enum

看看这个

// with a String Literal Type 
type MyKeyType1 = 'foo' | 'bar' | 'baz';

// or with a String Enum   
enum MyKeyType2 {
   FOO = 'foo',
   BAR = 'bar',
   BAZ = 'baz'
}

// or with a Const String Enum   
const enum MyKeyType3 {
   FOO = 'foo',
   BAR = 'bar',
   BAZ = 'baz'
}

var a : MyKeyType1 = "bar" 
var b: MyKeyType2 = MyKeyType2.BAR
var c: MyKeyType3 = MyKeyType3.BAR

将被转译为

// or with a String Enum   
var MyKeyType2;
(function (MyKeyType2) {
    MyKeyType2["FOO"] = "foo";
    MyKeyType2["BAR"] = "bar";
    MyKeyType2["BAZ"] = "baz";
})(MyKeyType2 || (MyKeyType2 = {}));
var a = "bar";
var b = MyKeyType2.BAR;
var c = "bar" /* BAR */;

如需进一步玩,您可以查看link

我更喜欢 const 枚举的情况,因为输入 Enum.Value 的方式很方便。 Typescript 将为我完成剩下的工作,以便在转译时获得最高性能。

【讨论】:

  • 感谢您提供的信息。我只是想说,当使用 const enum 时,你会失去 numeric enumsstring enums 的一些功能(例如,你不能循环他们不再)。如果您仍然尝试,TS 编译器会抱怨 'const' 枚举只能用于属性或索引访问表达式或导入声明或导出赋值或类型查询的右侧。通过您的示例,很明显为什么会出现这种情况。
  • 关于const enum 的一点说明:对于使用Babel 转译TS,从版本7 开始,我们不能再使用const enum。在他们修复它 (github.com/babel/babel/issues/8741) 之前,您可能会看到您的项目在迁移到更新的构建系统时中断。请注意,babel7 编译 TS 的速度比 babel6 快两倍,因此大多数开发人员可能会推动该迁移,并且必须将所有 const 枚举重构为枚举,在生产包中添加所有额外代码。
【解决方案3】:

枚举在开发时的一个好处是您可以通过智能感知轻松查看选项列表:

同样,您可以使用重构工具轻松更改枚举值,而不是到处更改字符串。

编辑:在 VS 2017 和 TypeScript >=3.2.4 中,智能感知适用于字符串文字类型:

【讨论】:

  • 好点,你在 Ryans 的回答中回答了我的问题,但他给了我更深的理解
  • 当然,我只是想补充他的答案:)
【解决方案4】:

枚举的一个很大的缺点是,如果你使用数字而不是字符串,那么在我看来,整个枚举并不安全:我总是可以将任何数字值赋给这种类型的变量

enum TYPE {MAN = 1, WOMAN = 2, BOY = 3, GIRL = 4};
let foo: TYPE = TYPE.MAN;
foo = 37.14; //no problem for compiler

【讨论】:

  • 您可以为变量分配任何值这一事实非常值得注意。也有点奇怪,怎么没有编译错误?
  • 这个缺点似乎是故意的,以支持位标志 - see answer here
【解决方案5】:

使用枚举代替字符串字面量的一个好处是,您也可以在未声明类型的地方使用它。

例如-

assert.equal(result.keyType, KeyType.FOO)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    相关资源
    最近更新 更多