Yehuda Makarov
Yehuda Makarov

Reputation: 605

Reducer<> type of Redux does not pass return type to the reducer properly?

Info: Using Typescript 3.3.3333 in Jetbrains Rider

Given this type definition of Reducer<State, Action>:

* @template S The type of state consumed and produced by this reducer.
* @template A The type of actions the reducer can potentially respond to.
*/
export type Reducer<S = any, A extends Action = AnyAction> = (
  state: S | undefined,
  action: A
) => S

Why will the return type of S not type check the state I am returning from the reducer, unless I define it explicitly?

export const INITIAL_STATE: AuthenticationState = {
    authenticationInProgress: false,
    accessToken: null,
    tokenType: null,
    expiryTimeMs: null,
    errorInformation: null,
    isAdmin: false,
};

export const authenticationReducer: Reducer<AuthenticationState, 
    AuthenticationActions> = (state = INITIAL_STATE, action): 
(why is this necessary??) ->  AuthenticationState => {
        switch (action.type) {
            case "authentication/user_authentication_request": {
                return {
                    ...state,
    problem -> hello: "hi",
                    authenticationInProgress: true,
                };
            }
...

Behavior I expect without needing to define AuthenticationState as the return value Behavior I expect without needing to define AuthenticationState as the return value

VS.

Why is there no more type checking on the return value if the Reducer Type includes the return value of S? Why is there no more type checking for the return value is the Reducer<S, A> Type includes the return value of S

Any light shedding and wisdom is really welcome. Thanks in advance.

Upvotes: 1

Views: 138

Answers (2)

Hitmands
Hitmands

Reputation: 14179

as Christian Santos stated, this is the expected behaviour.

If you want to have a type safe redux experience, then I suggest you to have a look at some redux extension that has been designed with that in mind!

check out redux-fluent

enter image description here enter image description here

Upvotes: 0

Christian Santos
Christian Santos

Reputation: 5456

TLDR: This is the expected behavior in TypeScript due to how excess property checking works.

In your case, the arrow function you define has a return type that has excess (or extra) properties. To TypeScript, that's completely OK.

In a simplified example, take a look at this behavior:

type a = () => { a: string };
type b = () => { a: string, b: string };

declare let myA: a;
declare let myB: b;

myA = myB; // OK! (excess properties are not checked)
myB = myA; // Error: missing required property 'b'

At the crux of your issue is that you're essentially assigning myB to myA and expecting an error, but you'll only get an error when assigning myA to myB.

You have a few options to make your code work as expected (such as the one you've proposed, where you explicitly define the return type), but none of them are ideal until TypeScript supports exact types (there's an open issue for exact types that discusses your exact use case w/ redux and some workarounds).

Upvotes: 1

Related Questions