I'm coming to you because I've exhausted all of the Google recommendations. I'm trying to create a websocket system that will handle statically typed requests and responses.
I'm generating the request/response types from another project. I have two base types (RequestData
and ResponseData
) and a "GenericRequest" that the user will interact with when trying to send new requests. An example looks like this:
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> {}
Here is an example of it's usage.
new HandshakeRequest({
AuthCode: "0",
SessionToken: "<jwt>"
}).send(socket).then((data) => {
// response handler logic
});
There is a function within GenericRequest<T,V>
called send()
that sends a request off and waits for a response. When the response comes back, it is supposed to convert the response json into a ResponseData
instance of type V
. I'm having trouble creating the new instance of ResponseData from the generic argument V
:
private create<R> (type: { new(): R} ) : R {
return new type();
}
^ This is the method of instantiating a generic type I've seen all over Stack Overflow, but it doesn't seem to work for me when I do this:
foo() : V { // where V corresponds to generic argument of GenericRequest<T,V>
return this.create<V>(V);
}
I get an error on the parameter within the parenthesis:
'V' only refers to a type, but is being used as a value here. ts(2693)
Here is the most minimum reproducible sample I could think of. I'd like to instantiate a new V
object where 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);
}
}
TypeScript's type system is erased when the code is transpiled into Javacript. At runtime, you can't call this.create(V)
or new V
because V
does not exist. (That's what the error is telling you). The solution you see elsewhere about requiring a parameter of type new()=>V
is meant to indicate that, at runtime, you need an actual constructor object that makes instances of type V
. Such a constructor object doesn't come into existence magically; anything that needs one of those will have to get it from somewhere. In your small Foo
example, I'd say it should be passed into the Foo
constructor, like this:
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
}
}
Of course that means you can't just call new Foo()
anymore.
const badFoo = new Foo(); // error, needs an argument
You'll need to pass it a no-arg constructor of the type you want your Foo
to make. For example:
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!!!
You can see that foo
is inferred to be of type Foo<Baz>
since you pass it the no-arg Baz
constructor. (Baz
does allow you to new
it with no arguments... the name
property will become the default value "Fred"
). And then foo.thisIsTheError()
successfully instantiates the Baz
class by calling its constructor with no argument. I made thisIstheError()
return the bar
value so you could see that it really is a class instance, and has the yell()
method.
Okay, hope that helps; good luck!