the-dr-lazy / deox

Functional Type-safe Flux Standard Utilities
https://deox.js.org
MIT License
206 stars 12 forks source link

DeepImmutable error while using ReturnType<typeof mapStateToProps> #55

Closed rlesniak closed 5 years ago

rlesniak commented 5 years ago

Any idea how to get rid of error Type 'DeepImmutableArray<string>' is missing the following properties from type... except import DeepImmutable from 'deox/dist/types' or explicty declare props?

Demo: https://codesandbox.io/s/vyqkr8y643?fontsize=14 Error in Component.tsx

the-dr-lazy commented 5 years ago

Hi @rlesniak Before any release for this issue as a hacky solution the best way I can imagine is something like below:

type InferState<T> = {
  [Key in keyof T]: T[Key] extends DeepImmutable<infer A> ? A : T[Key]
};

export type RootState = InferState<ReturnType<typeof rootReducer>>;

The above solution doesn't solve the actual problem. Can you figure out what should be the return type of the reducer that has been created by createReducer?

rlesniak commented 5 years ago

Thanks. So if I understand your question correctly, in my case it should be just:

type RootState = {
    status: Readonly<{
        error: string;
        details: StatusInfo;
        fetching: boolean;
    }>;
}

so only without DeepImmutableObject.

the-dr-lazy commented 5 years ago

The return type of a reducer should be what it actually returns and it is equal to return type of all handlers it has. So the return type of a reducer can be inferred from the return type of all handlers it has.

Also, because of the reactivity nature of the state in Flux, the return type of a reducer (which in the following called output/next state) should extend the input/prev state type. For now, the input/prev state type is immutable but it is going to also be mutable in #58 and according to the previous sentence the output/next state can be as follow:

  1. immutable or mutable when the input/prev state is immutable.
type NamesState = DeepImmutableArray<string>

const defaultNamesState: NamesState = Object.freeze([])

const namesReducer = createReducer(defaultNamesState, handleAction => [
  handleAction(actionA, (state, { payload }) => [...state, payload]), // string[]
  handleAction(actionB, (state) => state) // DeepImmutableArray<string>
])

namesReducer(...) // string[] | DeepImmutableArray<string>
  1. just mutable when the input/prev state is mutable.
type NamesState = string[]

const defaultNamesState: NamesState = []

const namesReducer = createReducer(defaultNamesState, handleAction => [
  handleAction(actionA, (state, { payload }) => [...state, payload]), // string[]
  handleAction(actionB, (state) => Object.freeze(state)) /* throws error
                                                            because of that input/prev state 
                                                            defined as a mutable array
                                                            but the output/next state is a `ReadonlyArray` */
])

namesReducer(...) // string[] 

@rlesniak @Haaxor1689 @LouizFC and other guys who interested in Deox, It would be great to have your opinion/feedback about the above solution because without that the growth of the Deox is not possible. If you are agree hit 👍; if not hit 👎 (Also it is awesome to leave a comment to describe your opinion/solution).

rlesniak commented 5 years ago

Are you sure its fixed? TSC still throws DeepImmutable error, check this sandbox mentioned in first post

the-dr-lazy commented 5 years ago

You are right and that is due to obligatory behavior of Deox in prev (input) state type which is wrapped by DeepImmutable (discussed in #58) and can't be fixed without breaking change. So if you agree, let's follow it in its specific place.

the-dr-lazy commented 5 years ago

Also, I found that combineReducer type signature in Redux isn't so accurate. I will try to send a PR to reduxjs/redux for fixing that.