You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript扩展Request类编译为ES5时触发TypeError问题求助

解决TypeScript扩展Request类编译到ES5时的TypeError问题

首先咱们得先搞清楚为什么会报错:当你用class ApiRequest extends Request并编译到ES5时,TypeScript会转译成基于ES5的继承模式(也就是通过Function.call/apply调用父类构造函数,再拼接原型链),但原生Request这类浏览器内置构造函数并不兼容这种ES5继承方式。这类内置对象的构造逻辑有特殊处理,直接用ES5的继承手法会导致this初始化异常,或者内置属性(比如body、headers)无法被正确修改/访问,从而抛出TypeError。

另外你代码里还有几个潜在问题:

  • 直接给this.body赋值:原生Request的body属性是只读的,你不能直接覆盖它;
  • 在调用super()之后修改headers:虽然这部分可能不是报错根源,但有些环境下内置Headers对象的修改也会有兼容性问题。

接下来给你两种可行的解决方案:

方案一:使用组合模式(推荐)

与其直接继承Request,不如包装它——创建一个类内部持有Request实例,对外暴露需要的方法和属性。这种方式兼容性最好,完全避开内置构造函数的继承问题:

export class ApiRequest {
  static userAgentHeader: string = '';
  static xUserAgentHeader: string = '';
  
  private request: Request;
  // 自定义获取body文本的getter
  async getBodyText(): Promise<string | null> {
    if (this.method !== 'GET' && this.request.body) {
      return await this.request.text();
    }
    return null;
  }
  // 按需暴露Request的原生属性
  get method(): string {
    return this.request.method;
  }
  get url(): string {
    return this.request.url;
  }
  get headers(): Headers {
    return this.request.headers;
  }

  constructor(url: string, initialData?: RequestInit) {
    // 先预处理headers,再传入Request构造
    const headers = new Headers(initialData?.headers);
    headers.set('User-Agent', ApiRequest.userAgentHeader);
    headers.set('X-User-Agent', ApiRequest.xUserAgentHeader);

    const init: RequestInit = {
      ...initialData,
      headers,
    };

    this.request = new Request(url, init);
  }

  // 转发Request的原生方法,比如clone
  clone(): ApiRequest {
    const clonedRequest = this.request.clone();
    return new ApiRequest(clonedRequest.url, {
      method: clonedRequest.method,
      headers: clonedRequest.headers,
      body: clonedRequest.body,
    });
  }

  static setAgentHeader(userAgentHeader: string, xUserAgentHeader?: string) {
    ApiRequest.userAgentHeader = userAgentHeader;
    ApiRequest.xUserAgentHeader = xUserAgentHeader || '';
  }
}

这种方式完全脱离了对内置构造函数继承的依赖,所有操作都基于原生Request实例,不管编译到ES5还是ES6+都不会有兼容性问题。

方案二:调整继承逻辑,适配ES5编译

如果你一定要用继承的方式,可以修改构造函数逻辑,避免直接修改只读属性,同时确保super()的调用符合内置构造函数的要求:

export class ApiRequest extends Request {
  static userAgentHeader: string = '';
  static xUserAgentHeader: string = '';

  constructor(url: string, initialData?: RequestInit) {
    // 提前处理headers,传给super()而非构造后修改
    const modifiedHeaders = new Headers(initialData?.headers);
    modifiedHeaders.set('User-Agent', ApiRequest.userAgentHeader);
    modifiedHeaders.set('X-User-Agent', ApiRequest.xUserAgentHeader);

    // 预处理初始化参数,GET方法自动忽略body
    const init: RequestInit = {
      ...initialData,
      headers: modifiedHeaders,
      ...(initialData?.method !== 'GET' ? {} : { body: undefined }),
    };

    // 必须先调用super(),这是TypeScript的语法要求
    super(url, init);

    // 用自定义getter替代直接赋值只读的body属性
    Object.defineProperty(this, 'bodyText', {
      get(): string | null {
        return init.body as string || null;
      },
      enumerable: true,
    });
  }

  static setAgentHeader(userAgentHeader: string, xUserAgentHeader?: string) {
    ApiRequest.userAgentHeader = userAgentHeader;
    ApiRequest.xUserAgentHeader = xUserAgentHeader || '';
  }
}

关键调整点:

  1. 提前处理headers:在调用super()之前就把headers修改好并传入Request构造,避免构造后修改内置Headers对象引发异常;
  2. 避开只读属性赋值:原生Request的body是只读的,改用自定义getter(比如bodyText)来获取你需要的文本内容;
  3. 兼容GET方法:GET方法下浏览器会自动忽略body,所以初始化时主动清除body参数避免冗余。

另外,如果你项目对ES5兼容性要求不高,可以尝试把TypeScript编译配置的target设为ES2015或更高,这样会使用原生ES6类继承,也能避开这个问题。

内容的提问来源于stack exchange,提问作者burtek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:21:25