Skip to content

Instantly share code, notes, and snippets.

Created September 10, 2017 20:38
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save anonymous/9ffb548a38b6c24114d4bad360bfe8f8 to your computer and use it in GitHub Desktop.
Save anonymous/9ffb548a38b6c24114d4bad360bfe8f8 to your computer and use it in GitHub Desktop.
create tagged union from return types of action-creators
// @flow
// Helper to extract inferred return type of a function
type _ExtractReturn<B, F: (...args: any[]) => B> = B;
type ExtractReturn<F> = _ExtractReturn<*, F>;
// Use constants as normal
const AGE = 'AGE';
const NAME = 'NAME';
// only need to provide types for arguments in action-creators
// return type will be inferred
function setAge(age: number) {
return { type: AGE, payload: age }
}
function setName(name: number) {
return { type: NAME, payload: name }
}
type State = { age: number, name: string }
// Create a union type containing all the return types of
// of your chosen action-creators. The result can be used as a tagged
// union that allows flow to narrow the payload type based on 'type' property
type Actions =
ExtractReturn<typeof setAge> |
ExtractReturn<typeof setName>
function reducer(state: State = { age: 0, name: '' }, action: Actions): State {
switch(action.type) {
case AGE: {
// here, flow knows action.payload is a number
return {
...state,
age: action.payload,
}
}
case NAME: {
// here, flow knows action.payload is a string
return {
...state,
name: action.payload.toLowerCase(),
}
}
default: return state;
}
}
@shakyShane
Copy link

Love to see any improvements over this as I'm new to Flow

@urban
Copy link

urban commented Sep 11, 2017

Can you elaborate on the ExtractReturn and _ ExtractReturn helpers? I'd like to use this technique to reduce my verbose typing however I'm still learning Flow and I don't fully understand how this works.

@traverse
Copy link

traverse commented Sep 13, 2017

@shakyShane I'm not sure it actually works. Looking at the example I see the following:

function setName(name: number /* <- Supposed to be a string. */) {
    return { type: NAME, payload: name }
}

setName's argument is supposed to be a string but it's currently not giving any errors, maybe I'm misunderstanding how this should work but to me this seems wrong.

@ronal2do
Copy link

in name is a string, line 16

@dfroger
Copy link

dfroger commented Nov 7, 2017

Using ExtractReturn seems to have problems. For example, if adding the error:

const payload: boolean = action.payload;

on line 32, the error is catched.

If added on line 39, the error is not catched.

Any idea how to fix it?

Related stackoverflow question.

Edit: this is not related to tye type of name (number vs string).

@wpcarro
Copy link

wpcarro commented May 9, 2018

I saw your SO question, dfroger. Thanks for reporting the issue.

I'm interested if anyone has found a way to reduce boilerplate, without sacrificing type safety? I'm looking to retrofit an existing codebase to use the ExtractReturn util, but it introduces too many false negative type errors, which is greatly disappointing.

Still would love a way to clean up the type noise...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment