With latest additions to TypeScript 2.8, we can get return type of any particular function definition! Let’s see how and why is it important for our action creators et al… shall we ?

Predefined conditional mapped types within standard library (lib.d.ts)

TypeScript 2.8 supports conditional types, which is a huge addition to the type checker! ( kudos to Mr. Anders Hejlsberg ).

I won’t get into details about them in this post as it deserves post on it’s own for sure. ( you can read more here )

Thanks to this addition, we can create new powerful mapped types, let’s say, for getting return type of a function, which we desperately need for our action creators.

What are mapped types? Read more here

Wait a second! It turns out we don’t have to create anything at all, ’cause they are already a part of TS 2.8! We’ve got following new mapped types at our disposal:

Exclude<T, U> -- Exclude from T those types that are assignable to U.

-- Exclude from T those types that are assignable to U. Extract<T, U> -- Extract from T those types that are assignable to U.

-- Extract from T those types that are assignable to U. NonNullable<T> -- Exclude null and undefined from T.

-- Exclude null and undefined from T. ReturnType<T> -- Obtain the return type of a function type.

-- Obtain the return type of a function type. InstanceType<T> -- Obtain the instance type of a constructor function type.

for more info (check out this PR)[https://github.com/Microsoft/TypeScript/pull/21847]

We will focus only on ReturnType<T> mapped type, which will get us return type of an action creator(a javascript function) which we have been all looking for! Let's see how.

ReturnType<T>

Let’s define our action type and action creator for setting a user age

Now we need to get somehow the return type of our FSA creator.

FSA = Flux Standard Action

Why we need the return type of action creator? We wanna leverage discriminant unions for 100% type safe reducers and for handling side effects via epics (redux-observable) or effects (@ngrx/store)

So as I’ve already mentioned billion times until now ( sorry :D ), we can leverage new mapped type ReturnType<T> :

Why typeof setAge ? We need to provide a type annotation as a generic argument to ReturnType mapped type. We can actually use a variable in a type annotation using the typeof operator. This allows you to tell the compiler that one variable is the same type as another. In our case we are capturing the type annotation of our setAge function

That’s it! Is it!?

…well when we look at the inferred implementation of SetAgeAction , we will see following type definition

{

type: string

payload: number

}

Oh no, where did our const type literal go? TypeScript flattened it to a string literal base type, which is string . We need to be explicit within our action creator, to make the proper type annotation flow correctly.