Open-source examples
Chapter 3: TypeScript for real-world applications
Chapters - Table of contents
Let's review together some real-world examples of usage of Generics and Function overloads from react and lodash.

React

useMemo()

useMemo() can be used as it follows:
1const items = useMemo(
2  () => ({ items: data.filter(/* ... */) }),
3  [data]
4)
5// `items` is of type `{ items: any[] }`
6
7
8// we can also pass a type parameter to add a constraint on the callback
9const items = useMemo<{ items: any }>(
10  // the function must be of type `() => { items: any[] }`
11  () => ({ items: data.filter(/* ... */) }),
12  [data]
13
14
1type DependencyList = ReadonlyArray<any>;
2/**
3 * `useMemo` will only recompute the memoized value when one of the `deps` has changed.
4 *
5 * Usage note: if calling `useMemo` with a referentially stable function, also give it as the input in
6 * the second argument.
7 *
8 * ```ts
9 * function expensive () { ... }
10 *
11 * function Component () {
12 *   const expensiveResult = useMemo(expensive, [expensive])
13 *   return ...
14 * }
15 * ```
16 *
17 * @version 16.8.0
18 * @see https://reactjs.org/docs/hooks-reference.html#usememo
19 */
20// allow undefined, but don't make it optional as that is very likely a mistake
21function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
22
As seen in this Chapter, useMemo<T>() leverages _Generics _in order to infer the return type of the given function as first parameter.

useCallback()

useCallback() can used as it follows:
1useCallback(
2    () => {
3      // do something
4    },
5    []
6)
7
8useCallback(
9    async () => {
10      // do something async
11    },
12    []
13)
14
which is typed by React as follows:
1/**
2 * `useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
3 * has changed.
4 *
5 * @version 16.8.0
6 * @see https://reactjs.org/docs/hooks-reference.html#usecallback
7 */
8// TODO (TypeScript 3.0): <T extends (...args: never[]) => unknown>
9function useCallback<
10  T extends (...args: any[]) => any
11>(callback: T, deps: DependencyList): T;
12
useCallback leverages _Generics _with some _type constraints _to ensure that the given callback is a function and, also to infer the return type of the given callback.

lodash

reduce()

Lodash's reduce() is a handy helper when it comes to deal with array manipulation.
Here are some examples:
1[].reduce(
2  (sum, curr) => sum + curr,
3  0
4) // return a `number` (inferred from the second argument)
5
6// return value can be of another type than the input type
7//  here `array` to `{ total: number }`
8
9// we need to pass `{ total: number }` to `reduce()` since
10//  it cannot infer it from the initial value argument (second argument)
11[].reduce<number, { total: number }>(
12  (acc, curr) => {
13    if (!acc.total) acc.total = 0
14
15    return acc.total = acc.total + curr
16  },
17  {}
18)
19
reduce() type definition is the following:
1/**
2 * Reduces a collection to a value which is the accumulated result of running each
3 * element in the collection through the callback, where each successive callback execution
4 * consumes the return value of the previous execution. If accumulator is not provided the
5 * first element of the collection will be used as the initial accumulator value. The callback
6 * is invoked with four arguments: (accumulator, value, index|key, collection).
7 * @param collection The collection to iterate over.
8 * @param callback The function called per iteration.
9 * @param accumulator Initial value of the accumulator.
10 * @return Returns the accumulated value.
11 */
12reduce<T, TResult>(
13  collection: T[] | null | undefined,
14  callback: MemoListIterator<T, TResult, T[]>,
15  accumulator: TResult
16): TResult;
17
18// ...
19
20type MemoListIterator<T, TResult, TList> = (
21  prev: TResult,
22  curr: T,
23  index: number,
24  list: TList
25) => TResult;
26
reduce() is a complete example of Generics and _Function overloads _capabilities:
  • nesting Generic: MemoListIterator<T, TResult, T[]>
  • type constraint on the collection argument: T[]
  • inferring types from arguments: TResult and T

get()

Given the following usage:
1let obj: { a: { b: number } } | undefined = { a: { b: 1 } }
2
3get(obj, ['a', 'b']) // => return type is `number | undefined`
4
5get({ a: { b: 1 } }, 'a.b')  // => return type is `any`
6
The corresponding lodash types for get() are as follows:
1/**
2 * Gets the property value at path of object. If the resolved value is undefined the defaultValue is used
3 * in its place.
4 *
5 * @param object The object to query.
6 * @param path The path of the property to get.
7 * @param defaultValue The value returned if the resolved value is undefined.
8 * @return Returns the resolved value.
9 */
10get<TObject extends object, TKey extends keyof TObject>(
11  object: TObject,
12  path: TKey | [TKey]
13): TObject[TKey];
14/**
15 * @see _.get
16 */
17get<TObject extends object, TKey extends keyof TObject>(
18  object: TObject | null | undefined,
19  path: TKey | [TKey]
20): TObject[TKey] | undefined;
21
22// ...
23
24get(object: any, path: PropertyPath, defaultValue?: any): any;
25
Simirarly to reduce(), get() is a complete example of Generics and Function overloads capabilities.
I encourage you to take a look at the complete definition to see how get() do support typing of the following:
1get({ a: { b: { c: 1  } }, [['a', 'b', 'c']]) // return type is `number`!
2
We use cookies to collect statistics through Google Analytics.
Do not track
 
Allow cookies