Reputation: 16450
So I have the following code:
type Something =
| { t: 'A', src: string }
| { t: 'B', src: string }
| { t: 'C', src: number };
const f = (o: Something): string =>
o.t === 1
? o.src
: 'a-string';
I expect to see an error when I return o.src
when o.t
is 1 since I never defined that t
can be a number. Therefore, flow doesn't know what the type of o.src
is if the t
is number. But it works without any error. What am I missing here?
Here is the flow/try.
For the record, the TypeScript version throws an error correctly. Though the error message is not so useful.
To clarify a bit more, if I use o.t === 'F'
instead, then flow will happily throw the following error:
9: o.t === 'F' ^ all branches are incompatible: Either object with property
t
that matches string literalF
1 is incompatible with string literalA
2. Or object with propertyt
that matches string literalF
1 is incompatible with string literalB
[3]. Or object with propertyt
that matches string literalF
1 is incompatible with string literalC
[4]. References: 9: o.t === 'F' ^ 1 4: | { t: 'A', src: string } ^ 2 5: | { t: 'B', src: string } ^ [3] 6: | { t: 'C', src: number }; ^ [4]
I don't know why, but it's just not consistent behavior and I expect to see some error if I'm using a value that not defined in the type. You know, like an unreachable piece of code.
Upvotes: 2
Views: 288
Reputation: 58715
This is down to the semantics of ===
. According to the ECMA specification, the operator is defined like this (at least the important bit):
The comparison x === y, where x and y are values, produces true or false. Such a comparison is performed as follows:
- If Type(x) is different from Type(y), return false.
- ...other steps...
You don't actually have to look past step 1. Your expression o.t === 1
can be seen as:
typeof o.t == typeof 1 && (...other steps...)
Only the first part of that expression will ever be evaluated because it can be shown to be false. Flow knows that you are never actually going to compare a number with a string here.
As other people have answered, you should use ==
with Flow instead.
Upvotes: 2
Reputation: 3478
If you check the type of o.src
at ? o.src
, you'll see it has an "Empty" type. That means that flow understands no valid set of inputs should result in this code being executed. Flow doesn't throw an error, but it does understand that case is not possible so only a string can be returned.
See Peter Hall's answer for why flow is doing this. It seems like the main reason is your use of the ===
operator. The first step of the ===
equality seems to be checking whether the type of two items is the same. Without them being the same, Flow marks the branch has empty. It seems like using ==
might be better for catching these things (as long as you have the unsafe coercion warnings turned on.)
Sidenote: if you're using Flow.com/try, you can hover over expressions to show you the type.
Upvotes: 1
Reputation: 220944
Flow generally wants you to use ==
since it can tell you about unsafe coercions anyway. If you write this example using ==
, you get an error (well, 4 errors) as expected:
const f = (o: Something): string =>
o.t == 1
? o.src
: 'a-string';
Upvotes: 2