Как добавить методы параметра типа к типу, использующему прокси

#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
  

(код в playground)

То, что я сделал, это создал экземпляр 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», поскольку это добавляет ненужный уровень выполнения в мое приложение?