I have an enum like this:
const Filter = {
ALL: 'ALL',
COMPLETED: 'COMPLETED',
UNCOMPLETED: 'UNCOMPLETED'
};
What I'd like to do is declare a union type like this:
type FilterType = Filter.ALL | Filter.COMPLETED | Filter.UNCOMPLETED
However, this fails, but I'm not sure why. According to the Flow docs:
When you create an object with its properties, you create a sealed object type in Flow. These sealed objects will know all of the properties you declared them with and the types of their values.
So, if I'm reading that correctly, Flow should be able to create a type from those values. Instead, it fails with:
Cannot use string as a type because string is a value. To get the type of a value use typeof.
Here's a link to a Flow Try with a possible solution (that I'm not happy with) and other approaches that have unsuccessfully worked.
I have an enum like this:
const Filter = {
ALL: 'ALL',
COMPLETED: 'COMPLETED',
UNCOMPLETED: 'UNCOMPLETED'
};
What I'd like to do is declare a union type like this:
type FilterType = Filter.ALL | Filter.COMPLETED | Filter.UNCOMPLETED
However, this fails, but I'm not sure why. According to the Flow docs:
When you create an object with its properties, you create a sealed object type in Flow. These sealed objects will know all of the properties you declared them with and the types of their values.
So, if I'm reading that correctly, Flow should be able to create a type from those values. Instead, it fails with:
Cannot use string as a type because string is a value. To get the type of a value use typeof.
Here's a link to a Flow Try with a possible solution (that I'm not happy with) and other approaches that have unsuccessfully worked.
Share Improve this question edited Jul 3, 2018 at 22:09 bbstilson asked Jul 3, 2018 at 21:57 bbstilsonbbstilson 1031 gold badge4 silver badges8 bronze badges 4-
type FilterType = 'ALL' | 'COMPLETED' | 'UNCOMPLETED'
...? I mean, if the constants are going to be named the same as their string values, there's no more hard-coding involved than there already is. You could alsoconst Filter: { [K in FilterType]: K } = { ... }
– Patrick Roberts Commented Jul 3, 2018 at 22:14 - "there's no more hard-coding involved than there already is". There's the chance of typos; I'd prefer to just reference the enum itself. – bbstilson Commented Jul 3, 2018 at 22:25
- You can't do that. As the error says, you're using a value as a type. Write your type first, and then implement your type with an object, or you don't even have to write an object at all, just use string literals. – Patrick Roberts Commented Jul 3, 2018 at 22:27
-
I hear what you're saying, but at the same time not having magic strings floating around is a better pattern. If I needed to change one of the values, I'd just change it in one spot instead of (in this case) two. All this said, I feel like the
$Keys<typeof Filter>
is the "right" way to do this in Flow. – bbstilson Commented Jul 3, 2018 at 22:32
1 Answer
Reset to default 9There's an easier solution. You can use $Values
utility to get union of value types. And in order to make flow resolve types as literal types instead of just string
the object should be frozen:
const FilterA = Object.freeze({
ALL: 'ALL',
COMPLETED: 'COMPLETED',
UNCOMPLETED: 'UNCOMPLETED'
});
type FilterTypeA = $Values<typeof FilterA>;
let a: FilterTypeA = FilterA.ALL; //OK
a = 'COMPLETED'; //OK
a = 'foo'; //$Expect error: string is inpatible with enum FilterTypeA
Try
This pattern works since 0.60.0 version