Home  >  Article  >  Web Front-end  >  TS function overloading - real world example

TS function overloading - real world example

王林
王林Original
2024-08-09 20:32:501105browse

Let's dive into less frequent Typescript feature - function overloading with a realistic example.

Intro

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.

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.

TS function overloading - real world example

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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn