Skip to content

Instantly share code, notes, and snippets.

@asolove
Last active October 25, 2017 18:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save asolove/550cca11eed1719e8ac6 to your computer and use it in GitHub Desktop.
Save asolove/550cca11eed1719e8ac6 to your computer and use it in GitHub Desktop.
// If I have a variant with two arms
type Foo = { type: "bar", bar: string } | { type: "baz", baz: string };
// It would be nice if a function could handle those two arms.
function fromThingy(foo: Foo): string {
switch(foo.type) {
case "bar":
return foo.bar;
case "baz":
return foo.baz;
}
}
// So that, if I ever add a third arm to that variant, this function will fail to typecheck until I think about how I should handle that variant.
// But right now, Flow seems to demand I add a default case.
// It errors with: "Return undefined. This type is incompatible with string"
// After asking the flowtype freenode channel, @marudor came up with an asesome workaround:
type Foo = { type: "bar", bar: string } | { type: "baz", baz: string };
function fromThingy(foo: Foo): string {
switch(foo.type) {
case "bar":
return foo.bar;
case "baz":
return foo.baz;
default:
return foo;
}
}
// Leaving out the default, the function doesn't typecheck. (Logically it should, but Flow doesn't know this.)
// Adding in a default, means the function will continue to type-check even if new variants are added. This takes away the whole point of having the compiler check that we've handled all the variants.
//
// If we want the function to pass type checking now, but fail to type-check when new variants are added,
// we can use this magic incantation `default: return foo` where foo must be the variable whose variant types
// are being matched on.
//
// At first I thought this was a quite silly hack and you ought to be able to return any type from the default case. After all,
// `foo` is a `Foo`, and this function has to return a `string`, so obviously Flow is just ignoring the type of the thing
// in the default case. But this is not true. Returning any value other than `foo` (or a string, obviously) fails.
//
// @marudor's suggested explanation is this: In each switch case, Flow learns more about `foo`. It starts out having to be
// one of two things, then as we pass the first two cases, we rule each of them out by turn. By the time we get to the default
// case, `foo` can't be either of the distjuncts, so it falls back to the base type: any. And any can be a string, so it works.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment