简体   繁体   中英

Extract return type and arguments types of a generic type

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM