与 TypeScript 一起使用

Mock Service Worker 通过 TypeScript 中的泛型参数促进类型安全的 API 模拟。使用通用参数,你可以注释路径参数、请求和响应主体类型、GraphQL 变量等内容。请参阅下面的使用示例。

¥Mock Service Worker facilitates type-safe API mocking through generic arguments in TypeScript. Using generic arguments, you can annotate things like path parameters, request and response body types, GraphQL variables, and more. Please see the examples of usage below.

我们强烈建议你探索从 msw 包导出的所有类型(corebrowsernode)。

¥We highly recommend exploring all the types (core, browser, and node) exported from the msw package.

请求处理程序

¥Request handlers

HTTP 处理程序

¥HTTP handlers

http 命名空间中的所有请求处理程序都支持三个通用参数:

¥All request handlers in the http namespace support three generic arguments:

http.get<Params, RequestBodyType, ResponseBodyType, Path>(path, resolver)
参数名称类型描述
Paramsobject请求路径参数。缩小 params 响应解析器参数类型。
RequestBodyTypeobject请求路径参数。缩小 request.json() 返回类型。
ResponseBodyTypeobject请求路径参数。缩小 HttpResponse.text()HttpResponse.json() 响应主体类型。
Pathstring请求路径。缩小请求处理程序上的 path 参数。
import { http, HttpResponse } from 'msw'
 
type AddCommentParams = {
  postId: string
}
 
type AddCommentRequestBody = {
  author: User
  comment: string
}
 
type AddCommentResponseBody = {
  commentUrl: string
}
 
http.post<
  AddCommentParams,
  AddCommentRequestBody,
  AddCommentResponseBody,
  '/post/:postId'
>('/post/:postId', async ({ params, request }) => {
  // Request path parameters are narrowed to the
  // provided "AddCommentParams" type.
  const { postId } = params
 
  // The request body JSON is narrowed to the
  // provided "AddCommentRequestBody" type.
  const commentData = await request.json()
  commentData.comment
 
  // The JSON response body type must satisfy
  // the "AddCommentResponseBody" type.
  return HttpResponse.json({
    commentUrl: `/post/${postId}?commentId=${crypto.randomUUID()}`,
  })
})

GraphQL 处理程序

¥GraphQL handlers

graphql 命名空间中的所有请求处理程序都支持三个通用参数:

¥All request handlers in the graphql namespace support three generic arguments:

graphql.query<Query, Variables>(query, resolver)
参数名称类型描述
QueryobjectGraphQL 操作响应查询。缩小 HttpResponse.json() 响应主体类型。
VariablesobjectGraphQL 操作变量。缩小 variables 响应解析器参数类型。
import { graphql, HttpResponse } from 'msw'
 
type AddCommentQuery = {
  commentUrl: string
}
 
type AddCommentVariables = {
  postId: string
}
 
graphql.mutation<AddCommentQuery, AddCommentVariables>(
  'AddComment',
  ({ variables }) => {
    // GraphQL variables are narrowed to the provided
    // "AddCommentVariables" type.
    const { postId } = variables
 
    // Response structure to this GraphQL mutation must
    // satisfy the provided "AddCommentQuery" type.
    // Note that the "data" key is implied.
    return HttpResponse.json({
      data: {
        commentUrl: `/post/${postId}?commentId=${crypto.randomUUID()}`,
      },
    })
  }
)

你可以利用 GraphQL 代码生成器 之类的工具根据你的 GraphQL 类型进行类型安全的模拟!

¥You can take advantage of tools like GraphQL Code Generator to have type-safe mocks based on your GraphQL types!

高阶请求处理程序

¥Higher-order request handlers

注释自定义请求处理程序将取决于你的高阶函数的调用签名。以下是几个例子。

¥Annotating custom request handlers will depend on the call signature of your higher-order functions. Here are a few examples.

首先,让我们看看如何使用 HttpResponseResolver 类型抽象出 resolver 函数,同时将其类型锁定在高阶处理程序中:

¥First, let’s see how you can abstract away a resolver function while locking its types within the higher-order handler, using the HttpResponseResolver type:

import { http, HttpResponseResolver, HttpResponse } from 'msw'
 
type SdkRequest = {
  transactionId: string
}
 
type SdkResponse = {
  transactionId: string
  data: { ok: boolean }
}
 
function handleSdkRequest(
  resolver: HttpResponseResolver<never, SdkRequest, SdkResponse>
) {
  return http.post('https://some-sdk.com/internal/request', resolver)
}
 
export const handlers = [
  handleSdkRequest(async ({ request }) => {
    const data = await request.json()
 
    // The response JSON body must satisfy the "SdkResponse"
    // imposed by the "handleSdkRequest".
    return HttpResponse.json({
      // The request body is narrowed to the "SdkRequest"
      // imposed by the "handleSdkRequest".
      transactionId: data.transactionId,
      data: { ok: true },
    })
  }),
]

了解有关以下 高阶响应解析器 的更多信息。

¥Learn more about the Higher-order response resolvers below.

库还公开了 HttpRequestHandlerGraphQLRequestHandler 类型来注释自定义函数,这些函数的调用签名应与 http.*graphql.* 请求处理程序的调用签名相同:

¥The library also exposes the HttpRequestHandler and GraphQLRequestHandler types to annotate custom functions that are meant to have the call signature identical to that of http.* and graphql.* request handlers:

import { http, graphql, HttpRequestHandler, GraphQLRequestHandler } from 'msw'
 
const myHttpHandler: HttpRequestHandler<Params, RequestBody, ResponseBody> = (
  path,
  resolver,
  options
) => {
  return http.get(path, resolver, options)
}
 
const myGraphQLHandler: GraphQLRequestHandler<Query, Variables> = (
  operationName,
  resolver,
  options
) => {
  return graphql.query(operationName, resolver, options)
}

高阶响应解析器

¥Higher-order response resolvers

使用 HttpResponseResolverGraphQLResponseResolver 类型注释自定义响应解析器。

¥Use the HttpResponseResolver and GraphQLResponseResolver types to annotate custom response resolvers.

import {
  PathParams,
  DefaultBodyType,
  HttpResponseResolver,
  delay,
  http,
  HttpResponse,
} from 'msw'
 
function withDelay<
  // Recreate the generic signature of the HTTP resolver
  // so the arguments passed to "http.get" propagate here.
  Params extends PathParams,
  RequestBodyType extends DefaultBodyType,
  ResponseBodyType extends DefaultBodyType
>(durationMs: number, resolver: HttpResponseResolver<Params, RequestBodyType, ResponseBodyType>): HttpResponseResolver<Params, RequestBodyType, ResponseBodyType> {
  return async (...args) => {
    await delay(durationMs)
    return resolver(...args)
  }
}
 
export const handlers = [
  http.get<never, never, 'hello world'>(
    '/resource',
    withDelay(250, ({ request }) => {
      // The "ResponseBodyType" generic type provided
      // to the "http.get()" request handler propagates
      // through the custom "withDelay" response resolver.
      return HttpResponse.text('hello world')
    })
  ),
]