Skip to content

Instantly share code, notes, and snippets.

@ygkn
Last active February 22, 2023 01:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ygkn/1b44051e8413355a91d2bdd765ae9b70 to your computer and use it in GitHub Desktop.
Save ygkn/1b44051e8413355a91d2bdd765ae9b70 to your computer and use it in GitHub Desktop.
import type { LowerHttpMethod, AspidaResponse } from "aspida";
import {
rest,
ResponseResolver,
RestContext,
RestRequest,
DefaultBodyType,
} from "msw";
/**
* aspida クライアントから型安全にハンドラを作成する
* 存在しないパスやメソッドを指定した場合や、レスポンスの型が合わない場合はコンパイルエラーになる
*
* レスポンスの型がつく(間違った型がエラーになる)のは正常系のレスポンスボディのみ
* ステータスコードや異常系のレスポンスボディは型がつかない(なんでも通る)ので注意
* (これは openapi2aspida が異常系を見ないこと、msw の ctx.status の引数が number なことが原因)
*
* @example
* ```ts
* import aspida from "@aspida/fetch";
* import { aspidaHandler } from "../aspidaHandler";
* import api from "@/api/$api";
*
* const hander = aspidaHandler(
* api(aspida()).users.login,
* "post",
* (req, res, ctx) => {
* return res(
* ctx.status(200),
* ctx.json({
* token: "eyJ....",
* })
* );
* }
* );
* ```
*
*/
export const aspidaHandler = <
// { $path: (option) => "/users", post: ... } のような型
Api extends Partial<Record<LowerHttpMethod, unknown>> & {
$path: (option?: unknown) => string;
},
// Api のプロパティのうち、LowerHttpMethod に含まれるもの ($path などを除く)
Method extends keyof Api & LowerHttpMethod
>(
api: Api,
method: Method,
// Api[Method] から RequestBody と ResponseBody を推論する
resolver: Api[Method] extends (options: {
body: infer ReqestBody extends DefaultBodyType;
}) => Promise<AspidaResponse<infer ResponseBody extends DefaultBodyType>>
? // RequestBody と ResponseBody を使って resolver の型を作成する
ResponseResolver<RestRequest<ReqestBody>, RestContext, ResponseBody>
: // 推論できない場合 (= パスやメソッドが存在しない場合) は never となり、コンパイルエラーになる
never
) => rest[method](api.$path(), resolver);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment