I want a type to describe a set of strings, and an object with keys for easy access to said strings.
When const TwoWords
is initialized with all of the keys' values type-asserted to TwoWords, then DoSomething(TwoWords.Foo)
compiles, but the typeguard on the switch statement doesn't work as expected - the type of word
in the default case is not never
.
type TwoWords = 'foo' | 'bar';
const TwoWords = {
Foo: 'foo' as TwoWords,
Bar: 'bar' as TwoWords
};
function DoSomething(word: TwoWords) {
switch (word) {
case TwoWords.Foo:
break;
case TwoWords.Bar:
break;
default:
let typeInferenceCheck: never = word; // Type '"foo"' is not assignable to type 'never'
}
}
DoSomething(TwoWords.Foo);
DoSomething('bar');
However, if I use a string literal type assertion for each of TwoWords
keys' values, the type of word
in the default case is never
as I would expect.
type TwoWords = 'foo' | 'bar';
const TwoWords = {
Foo: 'foo' as 'foo',
Bar: 'bar' as 'bar'
};
function DoSomething(word: TwoWords) {
switch (word) {
case TwoWords.Foo:
break;
case TwoWords.Bar:
break;
default:
let typeInferenceCheck: never = word; // OK
}
}
DoSomething(TwoWords.Foo);
DoSomething('bar');
In cases where 'foo'
and 'bar'
are much longer strings (say, a whole sentence), I don't want to duplicate them - it's too verbose. Is there another way to have a string keyed enum that behaves as expected from a type-inference perspective in a switch statement (or if/else chain)?
As per Madara Uchiha's answer, you can get proper type inference (as in Option 2) without the verbosity using TypeScript 2.4 string enums, but these aren't interchangeable with string literals.
DoSomething('bar'); // Type '"bar"' is not assignable to parameter of type 'TwoWords'
(See GitHub issue #15930 about string literal assignment to TypeScript 2.4 string enum)
I am looking for another option which allows me to have:
EnumLikeObject.Foo === 'foo'
let thing: EnumLikeObject = 'foo'
let thing: EnumLikeObject = EnumLikeObject.Foo
Objections & Discussion
Enums can also contain strings . This is more common than numeric enums, because of their readability and intent.
Enums are one of the few features TypeScript has which is not a type-level extension of JavaScript. Enums allow a developer to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases. TypeScript provides both numeric and string-based enums.
The they are useless at runtime argument This is a false argument for typescript in general, let alone Enums. and agree, if at runtime some code tries to change the values of one of your enums, that would not throw an error and your app could start behaving unexpectedly ( that is why Object.
In TypeScript, enums, or enumerated types, are data structures of constant length that hold a set of constant values. Each of these constant values is known as a member of the enum. Enums are useful when setting properties or values that can only be a certain number of possible values.
If you care to wait and endure for a bit,
TypeScript 2.4 brings true string enums to the playing field:
enum TwoWords {
Foo = 'foo',
Bar = 'bar'
}
function DoSomething(word: TwoWords) {
switch (word) {
case TwoWords.Foo:
break;
case TwoWords.Bar:
break;
default:
let typeCheck: never = word; // OK
}
}
That gets you the best of both worlds.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With