网站搜索

在 TypeScript 中,有没有办法实例化受约束的泛型类型的实例?


我来找你是因为我已经用尽了所有的谷歌推荐。我正在尝试创建一个 WebSocket 系统,以处理静态类型的请求和响应。

我正在从另一个项目生成请求/响应类型。我有两个基础类型(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 转换为类型为 V 的 ResponseData 实例。我在根据泛型参数 V 创建新的 ResponseData 实例时遇到了问题:

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 在 Foo<V> 中

class Foo<V> {
    constructor() {

    }

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

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

答案#1

当代码被转译成 JavaScript 时,TypeScript 的类型系统会被抹去。在运行时,你不能调用 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>,因为你传给它无参的 Baz 构造函数。(Baz 确实允许你用无参数 new 它……其 name 属性将变为默认值 "Fred")。然后 foo.thisIsTheError() 通过调用它的无参数构造函数成功实例化了 Baz 类。我让 thisIsTheError() 返回 bar 值,以便你可以看到它确实是一个类实例,并且有 yell() 方法。

好的,希望有帮助;祝你好运!

代码链接