Есть ли способ создания экземпляра экземпляра ограниченного универсального типа в TypeScript? - PullRequest
3 голосов
/ 29 октября 2019

Я иду к вам, потому что исчерпал все рекомендации Google. Я пытаюсь создать систему веб-сокетов, которая будет обрабатывать статически типизированные запросы и ответы.

Я генерирую типы запросов / ответов из другого проекта. У меня есть два базовых типа (RequestData и ResponseData) и «GenericRequest», с которым пользователь будет взаимодействовать при попытке отправить новые запросы. Пример выглядит следующим образом:

export class RequestData {}
export class ResponseData {}

export class GenericRequest<T extends RequestData, V extends ResponseData> {}
interface HandshakeResponseData extends ResponseData {
    AuthCode: string;
    RequestId: number; //unsigned
    RedirectUrl: string;
}

interface HandshakeRequestData extends RequestData {
    AuthCode: string;
    RequestId: number; //unsigned
    SessionToken: string;
}

export class HandshakeRequest extends GenericRequest<HandshakeRequestData, HandshakeResponseData> {}

Вот пример его использования.

new HandshakeRequest({
    AuthCode: "0",
    SessionToken: "<jwt>"
}).send(socket).then((data) => {
    // response handler logic
});

В GenericRequest<T,V> есть функция с именем send(), которая отправляетзапрос выключен и ждет ответа. Когда ответ возвращается, он должен преобразовать ответ json в ResponseData экземпляр типа V. У меня проблемы с созданием нового экземпляра ResponseData из универсального аргумента V:

private create<R> (type: { new(): R} ) : R {
        return new type();
}

^ Это метод создания универсального типа, который я видел во всем Stack Overflow, но это немне кажется, что это не работает, когда я делаю это:

foo() : V { // where V corresponds to generic argument of GenericRequest<T,V>
    return this.create<V>(V);
}

Я получаю сообщение об ошибке для параметра в скобках:

'V' only refers to a type, but is being used as a value here. ts(2693)

Вотсамый минимальный воспроизводимый образец, который я мог придумать. Я хотел бы создать новый объект V, где V in Foo<V>

class Foo<V> {
    constructor() {

    }

    create<V>(type: { new(): V} ) : V {
        return new type();
    }

    thisIsTheError() {
        let bar: V = this.create<V>(V);
    }
}

1 Ответ

2 голосов
/ 29 октября 2019

Система типов TypeScript стирается , когда код передается в Javacript. Во время выполнения вы не можете вызвать this.create(V) или new V, потому что V не существует. (Это то, что говорит вам ошибка). Решение, которое вы видите в другом месте о требовании параметра типа new()=>V, предназначено для указания того, что во время выполнения вам необходим фактический объект конструктора, который создает экземпляры типа V. Такой конструктор не существует магическим образом;все, что нуждается в одном из них, должно будет откуда-то получить его. В вашем небольшом примере Foo я бы сказал, что его следует передать в конструктор Foo, например:

class Foo<V> {

  // pass in the V constructor here:
  constructor(private ctor: new () => V) {

  }

  create<V>(type: { new(): V }): V {
    return new type();
  }

  thisIsTheError() {
    let bar: V = this.create(this.ctor); // okay now
    return bar; // return this just to show what it does
  }
}

Конечно, это означает, что вы больше не можете просто вызывать new Foo().

const badFoo = new Foo(); // error, needs an argument

Вам нужно будет передать ему конструктор без аргументов того типа, который вы хотите, чтобы ваш Foo сделал. Например:

class Baz {
  constructor(public name: string = "Fred") { }
  yell() {
    console.log("HEY MY NAME IS " + this.name.toUpperCase() + "!!!")
  }
}

const foo = new Foo(Baz); 
const baz = foo.thisIsTheError(); // okay, type is Baz
baz.yell(); // HEY MY NAME IS FRED!!!

Вы можете видеть, что foo имеет тип Foo<Baz>, так как вы передаете его конструктору no-arg Baz. (Baz позволяет вам new без аргументов ... name свойство станет значением по умолчанию "Fred"). И тогда foo.thisIsTheError() успешно создает экземпляр класса Baz, вызывая его конструктор без аргументов. Я заставил thisIstheError() вернуть значение bar, чтобы вы могли видеть, что это действительно экземпляр класса и имеет метод yell().

Хорошо, надеюсь, это поможет;удачи!

Ссылка на код

...