Примеры кода постарался максимально упростить, убрав лишнее.
Если кому-то будет интересно - доклад о таком подходе к реализации сервиса который мы пытаемся подружить с TypeScript
Есть сервис для работы с API (класс, экземпляр которого создаётся при старте приложения).
При создании ему передаются настройки, в том числе список эндпоинтов:
const apiClient = createApiClient({
endpoints: {
doTest: { url: '/test', method: 'get'},
},
});
С помощью
Proxy получается вместо
api.doRequest(endpoints.getData)
сделать что-то вроде виртуальных методов
api.getData()
Попытки реализовать это на TypeScript приводят либо к ошибке
TS2339: Property 'getData' does not exist on type 'ApiClient'
type RequestMethod = 'get'| 'post';
type Endpoint = {
method: RequestMethod;
url: string;
};
type EndpointsConfig = Record<string, Endpoint>;
class ApiClient {
endpoints: EndpointsConfig;
constructor(endpoints: EndpointsConfig) {
this.endpoints = endpoints;
}
doRequest(url: string, method: RequestMethod) : void {
console.log(`url: ${url}`);
console.log(`method: ${method}`);
}
}
function createApiClient(endpoints: EndpointsConfig) {
return new Proxy(new ApiClient(endpoints), {
get(target: ApiClient, name: keyof ApiClient) {
if (target.endpoints && target.endpoints[name]) {
return () => (
target.doRequest(target.endpoints[name].url, target.endpoints[name].method)
);
}
return target[name];
},
});
}
const apiClient = createApiClient({
getData: { url: '/data', method: 'get' },
});
apiClient.getData();
Либо к тому, что приходится определять эндпоинты не в конфиге, а непосредственно хардкодить в сервисе (что уже такое себе). При этом даже работает автокомплит, но
TS2722: Cannot invoke an object which is possibly 'undefined'.
import { Api } from './Api';
const endpoints = {
getData: { url: '/data', method: 'get' },
};
interface IEndpoint {
method: 'get'|'post';
url: string,
}
type EndpointsConfig = {
[K in keyof typeof endpoints]: IEndpoint;
};
type EndpointsProxy = {
[K in keyof EndpointsConfig]?: () => unknown;
}
type IApiProxy = { [K in keyof Api]: Api[K] } & EndpointsProxy;
function createApi() : IApiProxy {
return new Proxy(new Api(), {
get(target: Api, name: keyof Api) {
if (target.endpoints && target.endpoints[name]) {
return () => (
target.doRequest(target.endpoints[name])
);
}
return target[name];
},
});
}
const api = createApi();
api.getData();
или
TS2322: Type 'Api' is not assignable to type 'IApiProxy'. Property 'getData' is missing in type 'Api' but required in type 'EndpointsProxy'
если убрать необязательность методов-эндпоинтов
type EndpointsProxy = {
[K in keyof EndpointsConfig]: () => unknown;
}
Буду благодарен за помощь. Может быть я что-то не понимаю/не вижу, а может быть в целом такой подход не применим с TS.