为什么字符串文字与枚举不匹配

Swa*_*odo 0 enums typescript

我试图在打字稿中使用枚举,但它们的类型检查似乎不太一致。为什么我可以TestEnum.Foo === 'foo'在没有警告的情况下进行检查,但尝试传递'foo'到接受 a 的函数TestEnum会导致错误。

describe('Test enum functionality', () => {
  enum TestEnum {
    Foo = 'foo',
    Bar = 'bar'
  }

  // I expected this to work as TestEnum.Foo === 'foo'
  test('Can pass string to enum', () => {
    const func = (x: TestEnum) => {}
    // Error: Argument of type '"foo"' is not assignable to parameter of type 'TestEnum'
    func('foo');
  });

  // Surprised that this worked after I couldn't pass in a string literal to a function
  test('compiler can verify that this string literal is an Enum option', () => {
    if (TestEnum.Foo === 'foo') {

    }
  });

  // I expect an error here because the compiler should be able to see there is no overlap
  test('compiler can verify that this string literal is not an Enum option', () => {
    // Error: This condition will always return 'false' since the types 'TestEnum.Foo' and '"asdf"' have no overlap
    if (TestEnum.Foo === 'asdf') {

    }
  });
});
Run Code Online (Sandbox Code Playgroud)

更新

我删除了一些单元测试以更清楚地表明我感到困惑的原因

jca*_*alz 5

字符串值enum被认为是相关字符串文字的子类型。特别是, 的类型TestEnum.Foo可分配给字符串文字"foo",但反之则不然。这是价值观的一部分enum;它们应该按名称而不是按值使用。它是一个抽象层,允许您更改枚举的值,而无需重构其余代码。(由于历史原因,此限制未针对数字枚举实现,并且可用作位标志;对于数字枚举,您几乎可以将任何数字分配给枚举类型或从枚举类型分配任何数字。)

TestEnum和之间的不对称可分配性"foo" | "bar"func("foo")失败的原因。以下作业是安全的:

const okay: TestEnum = TestEnum.Foo; // okay
const alsoOkay: "foo" | "bar" = TestEnum.Foo; // okay, widening to supertype
Run Code Online (Sandbox Code Playgroud)

但这是一个错误,因为它缩小为子类型,编译器将其视为不安全:

const bad: TestEnum = "foo"; // error
Run Code Online (Sandbox Code Playgroud)

这类似于以下被视为不安全的方式:

const oops: string = Math.random() < 100 ? "hey" : 1; // error
Run Code Online (Sandbox Code Playgroud)

即使在运行时,oops它肯定是一个“字符串”,编译器只能验证它的类型string | number(好吧,"hey" | 1),所以它不能允许它。

如果你想这样做,你需要一个类型断言

const assertOkay: TestEnum = "foo" as TestEnum; // okay
Run Code Online (Sandbox Code Playgroud)

(意思func("foo" as TestEnum)是对你有用)


至于与 的比较===,TypeScript 检查(参见绝对庞大的文件isTypeEqualityComparableTo()中的函数)每个操作数的类型是否“相关”。通常,如果两种类型之一是另一种类型的子类型,则它们是相关的;如果一个值不可能同时属于两种类型,则它们是不相关的。(实际的检查要复杂得多,但这些给出了一个很好的经验法则。)这会导致以下行为:checker.ts

("foo" === TestEnum.Foo); // okay, TestEnum.Foo is a subtype of "foo"
("bar" === TestEnum.Foo); // error, no overlap between "bar" and TestEnum.Foo
const someTestEnum = Math.random() < 0.5 ? TestEnum.Foo : TestEnum.Bar;
("bar" === someTestEnum); // okay, overlapping types
Run Code Online (Sandbox Code Playgroud)

好的,希望有帮助;祝你好运!

链接到代码