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.

Javascript相关问答推荐

将自定义排序应用于角形数字数组

Next.js(react)使用moment或不使用日期和时间格式

未捕获错误:[]:getActivePinia()被调用,但没有活动Pinia.🍍""在调用app.use(pinia)之前,您是否try 使用store ?""

无法检测卡片重叠状态的问题

我在我的Java代码中遇到了问题,代码的一部分看不到先前定义的对象

在JS中拖放:检测文件

Chart.js-显示值应该在其中的引用区域

类构造函数不能在没有用With Router包装的情况下调用

在画布中调整边上反弹框的大小失败

使用Document.Evaluate() Select 一个包含撇号的HTML元素

用Reaction-RT-Chart创建实时条形图

我在哪里添加过滤器值到这个函数?

Chrome上的印度时区名S有问题吗?

如何将对象推送到firestore数组?

需要刷新以查看Mern堆栈应用程序中的更改

在使用JavaScript以HTML格式显示Google Drive中的图像时遇到问题

ReactJS在类组件中更新上下文

JavaScript -如何跳过某个字符(S)来打乱字符串中的字符

Reaction路由v6.4+数据API:重试加载程序而不显示错误

从D3 v3更新,没有错误,但输出SVG路径不可见