#typescript #ecmascript-6 #es6-proxy
#typescript #ecmascript-6 #es6-прокси
Вопрос:
Я разрабатываю простую клиент-серверную библиотеку RPC для своего внутреннего использования. Я хотел бы использовать один интерфейс для описания как клиента, так и сервера, чтобы я мог делиться ими между сервером и браузером в моем универсальном приложении.
Сервер прост, служба просто расширяет мой класс обслуживания и реализует интерфейс, описывающий API.
Клиент будет реализован как прокси ES6, просто отправляя все вызовы методов на сервер, я бы хотел, чтобы он был строго типизирован, а также класс service. Мне нужны экземпляры типа ServiceClient, чтобы иметь все методы и свойства его параметра типа, независимо от того, что это такое.
Я думал об этом синтаксисе:
interface Api
{
foo(): boolean;
}
class MyService extends Service implements Api
{
public foo(): boolean { return true; }
}
// usage
let result = (new ServiceClient<Api>()).foo(); // type of result is inferred, I get an error if I try to use an undefined method, arguments are type checked...
Я пытался просмотреть некоторые типы, где это работает аналогично, но не смог найти способ. Спасибо за вашу помощь.
Комментарии:
1. Что именно вы спрашиваете?
2. Как заставить ServiceClient<T> «наследовать» методы от T, без фактического наследования и необходимости их реализации.
3. Кажется, что в вашем коде отсутствуют некоторые части, что
Service
и чтоServiceClient
? Пожалуйста, поделитесь всем необходимым кодом.4. @NitzanTomer Я не знаю, что добавить в ServiceClient, кроме моей реализации прокси (что не имеет значения), это мой вопрос. Service — это обычный родительский тип, на данный момент он пуст, хотя в будущем он будет содержать конструктор, который регистрирует его на HTTP-сервере.
5. По сути, мне нужно каким-то образом изменить тип, который создается при вызове «new» — compile-time, он должен оставаться нормальным во время выполнения.
Ответ №1:
Надеюсь, я понимаю, о чем вы спрашиваете.
Вы не можете использовать аргумент generics ( T
) во время выполнения, поэтому вы не можете динамически создавать прокси-объект, который реализует все методы T
.
Что вы можете сделать, это что-то вроде этого:
interface Api {
foo(): boolean;
}
abstract class Service {}
class MyService extends Service implements Api {
public foo(): boolean { return true; }
}
const myService = new MyService();
const ServiceClient = {} as Api;
for (let key in myService) {
if (typeof myService[key] === "function" amp;amp; key !== "constructor") {
ServiceClient[key] = function() {
return myService[key].apply(myService, arguments);
}
}
}
let result = ServiceClient.foo(); // true
То, что я сделал, это создал экземпляр MyService
, затем я создал объект, который будет прокси для этого myService
, этот прокси ServiceClient
сначала создается как пустой объект.
Затем я перебираю свойства MyService
экземпляра и для каждой функции (которая не является конструктором) Я создаю функцию с тем же именем для прокси-объекта, когда вызывается каждая из этих функций, вызывается эквивалентная функция in myService
.
Редактировать
Если я понимаю, то все, что вам нужно сделать, это сообщить компилятору, что ваш экземпляр имеет тип Api
:
class ServiceClient {
// implement proxy magic here
}
let client = new ServiceClient() as any as Api;
let result = client.foo();
Это должно сработать, поскольку создает экземпляр класса, который прокси-вещи, а затем обманывает компилятор, думая, что это экземпляр Api
.
Комментарии:
1. Но класс ServiceClient не должен быть привязан к интерфейсу Api. Мне не нужна никакая поддержка во время выполнения, моя единственная цель — иметь строгую типизацию. Класс ServiceClient будет содержать прокси ES6, который будет отслеживать любые вызовы методов и передавать их на сервер через сокет. Мне нужен способ создать экземпляр ServiceClient, который имеет методы и свойства любого интерфейса, который я указываю в качестве его параметра типа.
2. Код, который я создаю, будет использоваться как внешняя библиотека, где пользователь будет предоставлять только интерфейс, описывающий API и класс обслуживания, который обрабатывает серверную часть, но не более того.
3. Мне нужен «новый ServiceClient<T> ()» для создания типа пересечения «ServiceClient amp; T», чтобы я мог использовать методы из T, а компилятор применяет строгую типизацию как для возвращаемого типа, так и для аргументов.
4. В typescript это невозможно. Для этого у вас могут быть только интерфейсы. Но если вам нужен класс (который вы можете создать), вам нужно заранее знать все методы.
5. Я создал функцию: «function createClient<T>(): JsonRpcServiceClient amp; T { return (new JsonRpcServiceClient()) как JsonRpcServiceClient amp; T; }», которая делает то, что мне нужно. Конечно, нет никакого способа сделать это с помощью ключевого слова «new», поскольку это добавляет ненужный уровень выполнения в мое приложение?