Skip to content
This repository has been archived by the owner on May 22, 2023. It is now read-only.

Autocomplete URLs by method #2

Merged
merged 1 commit into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ interface BaseParams {
query?: Record<string, unknown>;
}

export const methods = [ 'get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace' ] as const;
export type Method = (typeof methods)[number];

type TruncatedResponse = Omit<Response, 'arrayBuffer' | 'blob' | 'body' | 'clone' | 'formData' | 'json' | 'text'>;
/** Infer request/response from content type */
type Unwrap<T> = T extends {
Expand Down Expand Up @@ -68,6 +71,11 @@ export default function createClient<T>(defaultOptions?: ClientOptions) {
return res.ok ? { data: await res.json(), response } : { error: await res.json(), response };
}

/** Gets a union of paths which have method */
type PathsWith<M extends Method> = {
[Path in keyof T]: T[Path] extends { [ K in M ]: unknown } ? Path : never
}[keyof T];

type PathParams<U extends keyof T> = T[U] extends { parameters: any } ? { params: T[U]['parameters'] } : { params?: BaseParams };
type MethodParams<U extends keyof T, M extends keyof T[U]> = T[U][M] extends {
parameters: any;
Expand Down Expand Up @@ -139,35 +147,35 @@ export default function createClient<T>(defaultOptions?: ClientOptions) {

return {
/** Call a GET endpoint */
async get<U extends keyof T, M extends keyof T[U]>(url: T[U] extends { get: any } ? U : never, options: FetchOptions<U, M>) {
async get<U extends PathsWith<'get'>, M extends keyof T[U]>(url: U, options: FetchOptions<U, M>) {
return coreFetch(url, { ...options, method: 'GET' });
},
/** Call a PUT endpoint */
async put<U extends keyof T, M extends keyof T[U]>(url: T[U] extends { put: any } ? U : never, options: FetchOptions<U, M>) {
async put<U extends PathsWith<'put'>, M extends keyof T[U]>(url: U, options: FetchOptions<U, M>) {
return coreFetch(url, { ...options, method: 'PUT' });
},
/** Call a POST endpoint */
async post<U extends keyof T, M extends keyof T[U]>(url: T[U] extends { post: any } ? U : never, options: FetchOptions<U, M>) {
async post<U extends PathsWith<'post'>, M extends keyof T[U]>(url: U, options: FetchOptions<U, M>) {
return coreFetch(url, { ...options, method: 'POST' });
},
/** Call a DELETE endpoint */
async del<U extends keyof T, M extends keyof T[U]>(url: T[U] extends { delete: any } ? U : never, options: FetchOptions<U, M>) {
async del<U extends PathsWith<'delete'>, M extends keyof T[U]>(url: U, options: FetchOptions<U, M>) {
return coreFetch(url, { ...options, method: 'DELETE' });
},
/** Call a OPTIONS endpoint */
async options<U extends keyof T, M extends keyof T[U]>(url: T[U] extends { options: any } ? U : never, options: FetchOptions<U, M>) {
async options<U extends PathsWith<'options'>, M extends keyof T[U]>(url: U, options: FetchOptions<U, M>) {
return coreFetch(url, { ...options, method: 'OPTIONS' });
},
/** Call a HEAD endpoint */
async head<U extends keyof T, M extends keyof T[U]>(url: T[U] extends { head: any } ? U : never, options: FetchOptions<U, M>) {
async head<U extends PathsWith<'head'>, M extends keyof T[U]>(url: U, options: FetchOptions<U, M>) {
return coreFetch(url, { ...options, method: 'HEAD' });
},
/** Call a PATCH endpoint */
async patch<U extends keyof T, M extends keyof T[U]>(url: T[U] extends { patch: any } ? U : never, options: FetchOptions<U, M>) {
async patch<U extends PathsWith<'patch'>, M extends keyof T[U]>(url: U, options: FetchOptions<U, M>) {
return coreFetch(url, { ...options, method: 'PATCH' });
},
/** Call a TRACE endpoint */
async trace<U extends keyof T, M extends keyof T[U]>(url: T[U] extends { trace: any } ? U : never, options: FetchOptions<U, M>) {
async trace<U extends PathsWith<'trace'>, M extends keyof T[U]>(url: U, options: FetchOptions<U, M>) {
return coreFetch(url, { ...options, method: 'TRACE' });
},
};
Expand Down
58 changes: 58 additions & 0 deletions test/v1.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,64 @@ export interface paths {
};
};
};
"/anyMethod": {
get: {
responses: {
200: components["responses"]["User"];
404: components["responses"]["Error"];
500: components["responses"]["Error"];
};
};
put: {
responses: {
200: components["responses"]["User"];
404: components["responses"]["Error"];
500: components["responses"]["Error"];
};
};
post: {
responses: {
200: components["responses"]["User"];
404: components["responses"]["Error"];
500: components["responses"]["Error"];
};
};
delete: {
responses: {
200: components["responses"]["User"];
404: components["responses"]["Error"];
500: components["responses"]["Error"];
};
};
options: {
responses: {
200: components["responses"]["User"];
404: components["responses"]["Error"];
500: components["responses"]["Error"];
};
};
head: {
responses: {
200: components["responses"]["User"];
404: components["responses"]["Error"];
500: components["responses"]["Error"];
};
};
patch: {
responses: {
200: components["responses"]["User"];
404: components["responses"]["Error"];
500: components["responses"]["Error"];
};
};
trace: {
responses: {
200: components["responses"]["User"];
404: components["responses"]["Error"];
500: components["responses"]["Error"];
};
};
};
}

export type webhooks = Record<string, never>;
Expand Down
65 changes: 65 additions & 0 deletions test/v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,71 @@ paths:
$ref: '#/components/responses/Error'
500:
$ref: '#/components/responses/Error'
/anyMethod:
get:
responses:
200:
$ref: '#/components/responses/User'
404:
$ref: '#/components/responses/Error'
500:
$ref: '#/components/responses/Error'
put:
responses:
200:
$ref: '#/components/responses/User'
404:
$ref: '#/components/responses/Error'
500:
$ref: '#/components/responses/Error'
post:
responses:
200:
$ref: '#/components/responses/User'
404:
$ref: '#/components/responses/Error'
500:
$ref: '#/components/responses/Error'
delete:
responses:
200:
$ref: '#/components/responses/User'
404:
$ref: '#/components/responses/Error'
500:
$ref: '#/components/responses/Error'
options:
responses:
200:
$ref: '#/components/responses/User'
404:
$ref: '#/components/responses/Error'
500:
$ref: '#/components/responses/Error'
head:
responses:
200:
$ref: '#/components/responses/User'
404:
$ref: '#/components/responses/Error'
500:
$ref: '#/components/responses/Error'
patch:
responses:
200:
$ref: '#/components/responses/User'
404:
$ref: '#/components/responses/Error'
500:
$ref: '#/components/responses/Error'
trace:
responses:
200:
$ref: '#/components/responses/User'
404:
$ref: '#/components/responses/Error'
500:
$ref: '#/components/responses/Error'

components:
schemas:
Expand Down