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

MSW Resolver装饰器类型错误:可分配类型但可能实例化为不同子类型

MSW Resolver装饰器的TypeScript类型错误问题

我想通过response composition装饰MSW的resolver并返回一个新的resolver,编写了如下代码:

import {
  ResponseResolver,
  context,
  createResponseComposition,
  response
} from "msw";

export const createDelayedComposer = <
  M extends ResponseResolver<any, any, any>
>(
  milliseconds: number
) => {
  const delayed = createResponseComposition(undefined, [
    context.delay(milliseconds)
  ]);
  return (resolver: M): M => (req, _, ctx) =>
    resolver(req, Object.assign(delayed, response), ctx);
};

但出现了以下TypeScript错误:

Type '(req: any, _: ResponseComposition<any>, ctx: any) => AsyncResponseResolverReturnType<MockedResponse<any>>' is not assignable to type 'M'.
  '(req: any, _: ResponseComposition<any>, ctx: any) => AsyncResponseResolverReturnType<MockedResponse<any>>' is assignable to the constraint of type 'M', but 'M' could be instantiated with a different subtype of constraint 'ResponseResolver<any, any, any>'.ts(2322)

请问我的签名哪里出错了?该如何修复?或者是否有其他实现resolver装饰的方法?


错误原因

这个TypeScript错误的核心是泛型约束过宽且返回值类型不匹配:

  • 你定义的泛型M是ResponseResolver<any, any, any>的子类型,但返回的匿名函数只能匹配最宽泛的ResponseResolver<any, any, any>,无法保证和传入的具体M子类型完全兼容(比如M可能有更具体的请求/响应类型)。
  • 直接将匿名函数断言为M,TypeScript无法确认这个函数的结构和M的具体类型完全一致。

修复方案

需要更精确地拆分泛型参数,确保返回的resolver和传入的类型完全对齐:

import {
  ResponseResolver,
  context,
  createResponseComposition,
  response,
  ResponseComposition,
  DefaultBodyType,
  PathParams,
  RestRequest,
} from "msw";

export const createDelayedComposer = <
  B extends DefaultBodyType = DefaultBodyType,
  P extends PathParams = PathParams,
  R extends ResponseComposition<B> = ResponseComposition<B>
>(
  milliseconds: number
) => {
  const delayed = createResponseComposition<B>(undefined, [
    context.delay(milliseconds),
  ]);
  // 合并response并保持类型一致
  const enhancedResponse = Object.assign(delayed, response) as R;

  return (resolver: ResponseResolver<B, RestRequest<B, P>, R>): ResponseResolver<B, RestRequest<B, P>, R> => {
    return (req, res, ctx) => resolver(req, enhancedResponse, ctx);
  };
};

关键调整:

  1. 拆分ResponseResolver的三个泛型参数(响应体、请求参数、响应组合体),让类型推导更精确。
  2. 显式指定createResponseComposition的泛型类型B,确保响应体类型统一。
  3. 对合并后的enhancedResponse做类型断言,匹配传入resolver期望的响应组合体类型。
  4. 返回的函数明确标注为和传入resolver完全一致的ResponseResolver类型,消除类型不兼容问题。

替代实现:简洁的高阶函数包装

如果不需要极致的类型严谨性,可以用更简洁的方式实现,直接通过类型断言让TypeScript认可返回类型:

import {
  ResponseResolver,
  context,
  createResponseComposition,
  response,
} from "msw";

export const withDelay = (milliseconds: number) => {
  const delayed = createResponseComposition(undefined, [
    context.delay(milliseconds),
  ]);
  const enhancedResponse = Object.assign(delayed, response);

  return <T extends ResponseResolver<any, any, any>>(resolver: T): T => {
    return ((req, _, ctx) => resolver(req, enhancedResponse, ctx)) as T;
  };
};

这种写法代码更简洁,适合大多数场景,通过as T直接告知TypeScript返回的函数就是传入的resolver类型。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 16:05:29