我试图在打字稿中使用枚举,但它们的类型检查似乎不太一致。为什么我可以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)
我删除了一些单元测试以更清楚地表明我感到困惑的原因
字符串值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)
好的,希望有帮助;祝你好运!