In order to make it meaningful lets take a look at this mini naive implementation of what could look like react useReducer
type Reducer<S, A> = (prevState: S, action: A) => S;
type Dispatch<A> = (value: A) => void;
function useReducer<S, A>(reducer: Reducer<S, A>, initState: S): [getState: () => S, dispatcher: Dispatch<A>] {
let state = initState;
return [
() => state,
action => state = reducer(state, action)
];
}
// its working as expected (but that is not the question)
type CountActions =
| { type: "inc" }
| { type: "dec" }
| { type: "reset"; payload?: number };
const [getState, dispatch] = useReducer<number, CountActions>(
(state, action) => {
if (action.type === "inc") {
return state + 1;
}
if (action.type === "dec") {
return state - 1;
}
if (action.type === "reset") {
return action?.payload ?? 0;
}
return state;
},
0
)
getState()//0
dispatch({ type: 'inc' })
dispatch({ type: 'inc' })
getState()//2
dispatch({ type: 'dec' })
getState()//1
dispatch({ type: 'reset', payload: 2 })
getState()//2
dispatch({ type: 'reset' })
getState()//0
My question is: is there a way to write inner types of useReducer only deriving a single entry, the Reducer type?
How would you write ExtractSFrom
and ExtractAFrom
?
// ideally I want to have something that is defined like the real thing ;)
function useReducer<R>(reducer: R, initState: ExtractSFrom<R>): [getState: () => ExtractSFrom<R>, dispatcher: (value: ExtractAFrom<A>) => void] {
let state = initState;
return [
() => state,
action => state = reducer(state, action)
];
}
Thanks in advance
This is precisely what the infer
keyword is for. If you check out the types for the actual useReducer
hook, you'll see that React is inferring the type for the State
and the Action
from the reducer function.
type ReducerState<R extends Reducer<any, any>> = R extends Reducer<infer S, any> ? S : never;
type ReducerAction<R extends Reducer<any, any>> = R extends Reducer<any, infer A> ? A : never;
We specify that the generic R
must be some sort of reducer, aka R extends Reducer<any, any>
, and then work backwards to get either if its generic variables.
The useReducer
hook itself has a few different overloads, but basically it looks like this:
function useReducer<R extends Reducer<any, any>>(
reducer: R
): [ReducerState<R>, Dispatch<ReducerAction<R>>];
Where the generic is the reducer function and the types for State
and Action
are derived from it.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.