Вы могли бы действовать следующим образом:
// some interfaces you expect httpCall to return
interface User {
name: string;
age: number;
}
interface Payment {
id: string;
}
// a mapping of request paths to the function signatures
// you expect to return from createRequest
interface Requests {
"/users": (clause: { createdAfter: Date }) => Promise<Array<User>>;
"/payments": (id: string, clause: { createdAfter: Date }) => Promise<Payment>;
}
// a dummy httpCall function
declare function httpCall<R>(path: string, method: string, payload: any): R;
// for now only GET is supported, and the path must be one of keyof Requests
function createRequest<P extends keyof Requests>(method: "GET", path: P) {
return (function resourceApiCall(
...args: Parameters<Requests[P]> // Parameters<F> is the arg tuple of function type F
): ReturnType<Requests[P]> { // ReturnType<F> is the return type of function type F
return httpCall<ReturnType<Requests[P]>>(path, method, args);
} as any) as Requests[P]; // assertion to clean up createRequest signature
}
async function foo() {
const fetchUsers = createRequest("GET", "/users");
const users = await fetchUsers({ createdAfter: new Date() }); // User[]
const fetchPayment = createRequest("GET", "/payments");
const payment = await fetchPayment("id", { createdAfter: new Date() }); // Payment
}
В приведенном выше примере я использую интерфейс Requests
, чтобы указать на уровне типа отображение от пути запроса до нужной сигнатуры функции createRequest()
возвращать.И createRequest()
- это универсальная функция, использующая Requests
для строгого ввода возвращаемой функции.Обратите внимание, что внутри реализации resourceApiCall()
я также использую некоторые встроенные условные типы для извлечения типов аргументов и возвращаемого типа из сигнатуры функции.Это не является строго обязательным, но делает печатание внутри resourceApiCall()
более явным.
В любом случае, надеюсь, это поможет.Удачи!
ОБНОВЛЕНИЕ: Вот возможный способ разбить это на разные модули так, чтобы каждый модуль касался только своей конечной точки.
Сначала, получите файл с createRequest()
в нем вместе с изначально пустым интерфейсом Requests
:
Requests / reports.ts
export interface Requests extends Record<keyof Requests, (...args: any[]) => any> {
// empty here, but merge into this
}
// a dummy httpCall function
declare function httpCall<R>(path: string, method: string, payload: any): R;
// for now only GET is supported, and the path must be one of keyof Requests
export function createRequest<P extends keyof Requests>(method: "GET", path: P) {
return (function resourceApiCall(
...args: Parameters<Requests[P]> // Parameters<F> is the arg tuple of function type F
): ReturnType<Requests[P]> {
// ReturnType<F> is the return type of function type F
return httpCall<ReturnType<Requests[P]>>(path, method, args);
} as any) as Requests[P]; // assertion to clean up createRequest signature
}
Затем вы можете сделать модуль для вашего User
вещи:
запросы / user.ts
export interface User {
name: string;
age: number;
}
declare module './requests' {
interface Requests {
"/users": (clause: { createdAfter: Date }) => Promise<Array<User>>;
}
}
и ваши Payment
материалы:
запросы / payment.ts
export interface Payment {
id: string;
}
declare module './requests' {
interface Requests {
"/payments": (id: string, clause: { createdAfter: Date }) => Promise<Payment>;
}
}
и так далее.Наконец, пользователь может вызвать их, импортировав createRequest
и, возможно, модули user
и payment
(если в них есть код, который вам нужно запустить в своем модуле):
test.ts
import { createRequest } from './Requests/requests';
import './Requests/user'; // maybe not necessary
import './Requests/payment'; // maybe not necessary
async function foo() {
const fetchUsers = createRequest("GET", "/users");
const users = await fetchUsers({ createdAfter: new Date() }); // User[]
const fetchPayment = createRequest("GET", "/payments");
const payment = await fetchPayment("id", { createdAfter: new Date() }); // Payment
}
Хорошо, надеюсь, это поможет снова.