Let's dive into less frequent Typescript feature - function overloading with a realistic example.
Have a custom hook
export function useUrlState<T extends JSONCompatible>( defaultState: T, searchParams?: object, )
At some moment I need to add more arguments to it, possibly more in the future. Hard to remember what Nth argument is, and calling a function like useUrlState(firstArg, null, null, fourthArg) is ridiculous. It will be way easier to pass arguments inside an object like this:
export function useUrlState<T extends JSONCompatible>({ defaultState, searchParams, replace }: { defaultState: T, searchParams?: object, replace?: boolean })
I will convert the function to a new format and keep it backward compatible with the existing implementation.
First, need to add overload signatures right above function implementation. Overload signatures are all possible ways a function can be called, with different argument's type and quantity.
/** * @deprecated Pass arguments in a object `useUrlState({ defaultState: form, searchParams })` * * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/next/useUrlState#api} */ export function useUrlState<T extends JSONCompatible>(defaultState: T, searchParams?: object): { state: DeepReadonly<T>, updateState: (value: Partial<DeepReadonly<T>>, updateUrl: (value?: Partial<DeepReadonly<T>>) => void, getState: () => DeepReadonly<T> } /** * NextJS hook. Returns `state`, `updateState`, and `updateUrl` functions * * @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state * @param {?SearchParams<T>} [searchParams] searchParams from Next server component */ export function useUrlState<T extends JSONCompatible>({ defaultState, searchParams }: { defaultState: T, searchParams?: object, replace?: boolean }): { state: DeepReadonly<T>, updateState: (value: Partial<DeepReadonly<T>>) => void, updateUrl: (value?: Partial<DeepReadonly<T>>) => void, getState: () => DeepReadonly<T> } // <- notice that should implicitly define returned value // implementation export function useUrlState<T extends JSONCompatible>( defaultState: T | { defaultState: T, searchParams?: object, replace?: boolean }, searchParams?: object, ) {
Tricky part is that signatures should be compatible with implementation, so have this defaultState: T | { defaultState: T, searchParams?: object, replace?: boolean }
I assume that if the first argument has a specific key, it is a new object format.
const _defaultState = ('defaultState' in defaultState ? defaultState.defaultState : defaultState) as T const _searchParams = ('defaultState' in defaultState ? defaultState.searchParams : searchParams) as object | undefined const _replace = ('defaultState' in defaultState ? defaultState.replace ?? true : false) as boolean
Also, can notice that replace argument has default value true for a new format, but for old one it's false.
Let's see how it works.
Notice that we have different JSDoc comments for each signature, old one marked with @deprecated tag.
Official docs https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads
Tnx for reading :)
Leave a comment about your experience, or if you have ideas how to do it more elegantly.
The above is the detailed content of TS function overloading - real world example. For more information, please follow other related articles on the PHP Chinese website!