Search code examples
typescriptgenericsngrx

Meaning of NgRx createAction method's return type signature


I have been going through NgRx Doumentation for createAction method as in below link : Overloads for createAction method

I am not able to understand below in type signature of this method, specifically the return Type of createAction method : What is

() => TypedAction<T>

in this signature :

 ActionCreator<T, () => TypedAction<T>>

I don't see any reference to TypedAction? Does it mean any object of shape of specific Action type?

What I understand by T in above signature of return type is that it is generic type for ActionCreator function which will return Action of type T when called. But Not sure the other Type argument indicates except that it seems to be some function which is returning TypedAction of type T. Would like to know a real world example.


Solution

  • TypedAction is a generic interface which extends Action type by adding a readonly type property.

    export declare interface TypedAction<T extends string> extends Action {
      readonly type: T;
    }
    

    ActionCreator<T, () => TypedAction<T>> - tell us that we have a factory which returns a TypedAction object () => ({ type: T})

    Let's define an action creator:

    export const logout = createAction('[Auth] Logout');
    

    createAction function is defined in action_creator.ts.

    export function createAction<T extends string>(
      type: T
    ): ActionCreator<T, () => TypedAction<T>>;
    

    from the declaration we can tell that createAction will return a function which in turn returns an object with a type property of string in our case as <T extends string>

    Let's dive into the actual implementation a bit. When you don't provide a payload for your action creator the following code is executed:

    export function createAction<T extends string, C extends Creator>(
      type: T,
      config?: { _as: 'props' } | C
    ): Creator {
    ...
        case 'empty':
          return defineType(type, () => ({ type }));
    ...
    }
    

    And defineType is:

    function defineType(type: string, creator: Creator): Creator {
      return Object.defineProperty(creator, 'type', {
        value: type,
        writable: false,
      });
    }
    

    defineType accepts a type ('[Auth] Logout') and a Creator - () => ({ type }). It returns back Creator but with a new property type. So calling logout.type and logout().type will return the same value - '[Auth] Logout'

    Later on, in the reducer_creator.ts it allows us to extract ActionCreator type ("[Auth] Logout" in our case), associate it to the reducer function and execute it

    UPDATE: As the answer to question became to big, I've decided to write a blog post How do NgRx Action Creators work