Home > OS >  How do I have typescript have the type properly infered when using a generic initializer function?
How do I have typescript have the type properly infered when using a generic initializer function?

Time:02-01

I have a function that may take a parameter in the form of a callable like this:

type Initial<T> = T | PromiseLike<T> | (() => T) | (() => PromiseLike<T>)

const usePromise = <T>(initial: Initial<T>): T => {
  let result: unknown = null
  
  /* ... */
  
  return result as T
}

If the type of initial is something like Promise<number> or PromiseLike<number>, typescript correctly infers the type of T as number. However, if initial is of a function type like () => PromiseLike<number>, typescript infers T as PromiseLike<number>. For example:

// good:
const initial1 = new Promise<number>(() => null)
const result1 = usePromise(initial1)  // result1: number (OK)

// bad:
const initial2: () => PromiseLike<number> = () => new Promise(() => null)
const result2 = usePromise(initial2)  // result2: PromiseLike<number> (ERROR, should be number)

Note: obviously these promises would never resolve nor reject, but this question is purely about the type inference.

How do I get typescript to also correctly infer the type in the second example?

CodePudding user response:

The union presents multiple valid inference targets. The singular T type is allways valid and TypeScript propably does not put in the computation effort to see that (() => PromiseLike<T>) is also a valid target. We need to explicitly give an inference order to make the compiler examine more specific types like (() => PromiseLike<T>) first. I would suggest the use of overloads.

The overloads are resolved in order. The first overload is examined before the second one etc...

type Initial<T> = T | PromiseLike<T> | (() => T) | (() => PromiseLike<T>)

function usePromise<T>(initial: (() => PromiseLike<T>)): T
function usePromise<T>(initial: (() => T)): T
function usePromise<T>(initial: PromiseLike<T>): T
function usePromise<T>(initial: T): T
function usePromise<T>(initial: Initial<T>): T {
  let result: unknown = null
  
  /* ... */
  
  return result as T
}

Playground

  • Related