Open1
slug付きのエンドポイントをTypeScriptで型制約する方法
request.ts
export const REQUEST_PATHS = {
GET_HOGE: '/api/hoge/[hogeId]'
} as const;
type PathKey = keyof typeof REQUEST_PATHS;
type Path = typeof REQUEST_PATHS[PathKey];
type WithoutSlash<T> = T extends `/${infer U}` ? U : never;
type Resource<T> = T extends `${infer U}/${infer S}` ? U | Resource<S> : T;
type DynamicRoute<T> = T extends `[${infer U}]` ? U : never;
type Params<T> = DynamicRoute<Resource<WithoutSlash<T>>>;
type ParamKeys<T extends Path> = Params<T>;
type PathParams<T extends Path> = {
path: T;
params?: { [K in ParamKeys<T>]: string | number };
};
type Args<T extends Path> = ParamKeys<T> extends never
? PathParams<T>
: Required<PathParams<T>>;
export type RequestPath = typeof REQUEST_PATHS[keyof typeof REQUEST_PATHS];
export function createPath<T extends Path>({ path, params }: Args<T>) {
if (!params) return path;
return path
.split('/')
.map((str) => {
const match = str.match(/\[(.*?)\]/);
if (match) {
const key = match[0];
const trimmed = key.substring(1, key.length - 1) as ParamKeys<
typeof path
>;
return params[trimmed];
}
return str;
})
.join('/');
}
index.ts
const { data } = await request.get<Hoge>(
createPath({
path: REQUEST_PATHS.SEEK_THUMBNAIL,
params: {
hogeId: '1',
},
})
)
console.log(data)
参考記事