简体   繁体   中英

TypeScript overloaded generic function's return type inferred incorrectly when used with Array.prototype.map

In the following code, the only difference between getDocsWithIdThatWorks and getDocsWithIdThatComplains is the callback they are passing to Array.prototype.map . In one case it's a function expression, and in the other it's an overloaded, generic function. In the first case, TypeScript perfectly understands and agrees about the return type of the overloaded function. But when passing the overloaded function's reference directly as an argument to Array.prototype.map , TypeScript suddenly infers the return type as DocumentWithId<unknown> , despite the fact that the array being mapped is clearly typed as QueryDocumentSnapshot<T> . Why is that so?

Playground link

interface DocumentSnapshot<T> {
    id: string
    data(): T | undefined
}

interface QueryDocumentSnapshot<T> extends DocumentSnapshot<T> {
    data(): T
}

interface DocumentWithId<T> {
  id: string
  data?: T
}

interface QueryDocumentWithId<T> {
  id: string
  data: T
}


function withId<T>(document: QueryDocumentSnapshot<T>): QueryDocumentWithId<T>
function withId<T>(document: DocumentSnapshot<T>): DocumentWithId<T>
function withId<T>(document: DocumentSnapshot<T> | QueryDocumentSnapshot<T>): DocumentWithId<T> | QueryDocumentWithId<T> {
  return {
    id: document.id,
    data: document.data(),
  }
}

type Query<T> = () => Promise<QueryDocumentSnapshot<T>[]>

async function getDocsWithIdThatWorks<T>(query: Query<T>): Promise<QueryDocumentWithId<T>[]> {
  const result = await query()
  return result.map((d) => withId(d))
}

async function getDocsWithIdThatComplains<T>(query: Query<T>): Promise<QueryDocumentWithId<T>[]> {
  const result = await query()
  return result.map(withId)
}

This is a known design limitation of TypeScript; see microsoft/TypeScript#35501 . TypeScript can only properly resolve call signatures for overloaded functions when such functions are actually called .

When you write d => withId(d) , you are actually calling withId with a parameter of type QueryDocumentSnapshot<T> , and the compiler selects the first call signature, as desired.

But when you just pass withId to map() , you are not calling withId and so the proper overload resolution does not happen. Instead the compiler just "gives up" and picks the last signature. And that results in the error you're seeing.

This happens whenever the compiler has to infer a type involving an overloaded function that it does not call. I don't see direct documentation about this for generic type inference, but it is mentioned in the documentation about conditional type inference , where it says:

When inferring from a type with multiple call signatures (such as the type of an overloaded function), inferences are made from the last signature (which, presumably, is the most permissive catch-all case). It is not possible to perform overload resolution based on a list of argument types.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM