【问题标题】:XHR Interceptor in TypescriptTypescript 中的 XHR 拦截器
【发布时间】:2022-01-28 09:31:30
【问题描述】:

为什么 Typescript 中不允许使用以下猴子补丁?

const oldXHROpen = window.XMLHttpRequest.prototype.open

window.XMLHttpRequest.prototype.open = function (
    method: string,
    url: string
): void {
    return oldXHROpen.apply(this, [method, url])
}

它给出了以下错误:

Argument of type '[string, string]' is not assignable to parameter of type '[method: string, url: string | URL, async: boolean, username?: string | null | undefined, password?: string | null | undefined]'.
  Source has 2 element(s) but target requires 3.

但是在查看open 的定义时,有一个方法只需要两个参数。

open(method: string, url: string | URL): void;

【问题讨论】:

  • 这是 TypeScript 的设计限制,请参阅 ms/TS#38353。当处理具有多个调用签名的函数类型时,编译器倾向于删除除其中一个之外的所有函数类型(通常是最后一个)。如果您关心解决方法,您可以扩大/断言oldXHROpen 具有您关心的一个呼叫签名,例如this。请注意仍然存在错误,因为该重载不接受超过 2 个参数。
  • 如果它满足您的需求,我可以将其写为答案,但我想知道您是否不介意 editing 示例以摆脱 args(如 this)因为它与所问的问题没有直接关系,只会导致分散注意力的不同错误。如果它不能满足您的需求,请告诉我缺少什么。
  • 好的,谢谢你的解释。我编辑了我的帖子以摆脱额外的args-error。请提供您的答案,我会接受。

标签: javascript typescript xmlhttprequest


【解决方案1】:

这是 TypeScript 的设计限制;请参阅microsoft/TypeScript#38353 了解更多信息。

the XHMLHttpRequest.open() methodTypeScript standard library's typings 如下所示:

interface XMLHttpRequest {
    open(method: string, url: string | URL): void;
    open(method: string, url: string | URL, async: 
      boolean, username?: string | null, password?: string | null): void;
}

有两个调用签名,这使得 TypeScript 中的 open() 成为 overloaded 方法。如果直接调用重载的方法或函数,编译器会根据你传入的参数选择其中一个重载。

但它无法仅在类型级别上做到这一点。如果您开始以编程方式操作重载函数类型的类型,编译器将立即选择一个调用签名,而不会尝试找出最适合该情况的调用签名。通常这是最后一次调用签名。

在这种情况下,假设您启用了the --strictBindCallApply compiler optionthe apply() method for CallableFunction 只会看到XHMLHttpRequest.open() 的第二个调用签名。该调用签名至少需要三个参数,因此当您apply() 其中两个参数时编译器会生气。哎呀。


在这种情况下,解决方法是让您手动将 oldXHROpen 的类型从一对调用签名扩大到您尝试支持的单个调用签名。由于扩展是一种安全操作,因此您可以使用 type annotation 执行此操作,并且不要放弃任何类型安全:

const oldXHROpen: (method: string, url: string | URL) => void =
  window.XMLHttpRequest.prototype.open // okay

一旦你这样做了,apply() 方法只会看到你关心的 open() 调用签名,并且一切正常:

window.XMLHttpRequest.prototype.open = function (
  method: string,
  url: string
): void {
  return oldXHROpen.apply(this, [method, url])  // okay
}

Playground link to code

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多