React Typescript Generic Type Overloading (e.g. useState)

I am attempting to create an overloaded function that will write to and from session storage instead of the standard state. I’d like it to be overloadable in the instances where I only provide a type and not an initial value. Exactly like what useState does in react.

Here is what I have so far:

export function useSessionState<S = undefined>(initialState?: S): [S, (state: S) => void];
export function useSessionState<S>(initialState: S): [S, (state: S) => void] {
  const [state, setState] = useState<S>()

  // Unimportant: Do something to load to and from session storage.

  return [state, (value) => {

  }]
}

When I try to consume this type I only ever get the result type of one of the overloads.

// Variable 'state' is a string as expected.
const [state, setState] = useSessionState("some_initial_value")

// Variable 'undefinedState' is also a string, but it should be undefined since no 'initialState' was provided.
const [undefinedState, setUndefinedState] = useSessionState<string>()

1 thought on “React Typescript Generic Type Overloading (e.g. useState)”

  1. I didn’t know the answer myself, so I started reading and experimenting.

    The best solution I’ve found so far is this (Playground):

    function useSessionState<T>(initialState: T): [T, (state: T) => void];
    function useSessionState<T = undefined>(): [T | undefined, (state: T) => void];
    function useSessionState<T = undefined>(initialState?: T): [T extends undefined ? undefined | T : T, (state: T) => void] {
      // Unimportant code
    }
    
    const [state1, setState1] = useSessionState<string>("initialValue"); // string
    const [state2, setState2] = useSessionState("initialValue"); // string
    const [state3, setState3] = useSessionState<string>(); // undefined | string
    const [state4, setState4] = useSessionState(); // undefined
    

    Note: the .d.ts that is generated on the playground:

    declare function useSessionState<T>(initialState: T): [T, (state: T) => void];
    declare function useSessionState<T = undefined>(): [T | undefined, (state: T) => void];
    

    But there might be easier solutions…

    Reply

Leave a Comment