简体   繁体   English

Typescript:将泛型类传递给函数参数

[英]Typescript: Pass generic class to function parameter

I need to pass a class reference as a function parameter and invoke a static method on this class. 我需要将class reference作为function parameter传递,并在此类上调用static method In the future, I might need to create an instance of the class and added a constructor as example. 将来,我可能需要创建类的实例并添加构造函数作为示例。

I have it working without typings: 我没有打字工作:

class Messages {
    getMessage(classRef: any): any {
       return classRef.getMessage()
     }
}

class myClassA {
    constructor(public id: number, public message: string) {}
    static getMessage(): string {
        return 'hello from class A';
    }
}

class myClassB {
    constructor(public id: number, public message: string) {}
    static getMessage(): string {
        return 'hello from class B';
    }
}

var messages = new Messages();
var messageA = messages.getMessage(myClassA);
var messageB = messages.getMessage(myClassB);

console.log(messageA) // 'hello from class A'
console.log(messageB) // 'hello from class B'

I am trying to type the class reference, possibly with generics but confused about how to go about this. 我试图键入类引用,可能与泛型,但混淆如何去做。

I tried doing something along the lines of getMessage<C>(classRef: {new(): C;}): any {}... but none of that is working. 我尝试按照getMessage<C>(classRef: {new(): C;}): any {}...做一些事情getMessage<C>(classRef: {new(): C;}): any {}...但这些都没有用。

Can someone please explain how I can pass a class reference properly? 有人可以解释我如何正确传递课程参考?

Generally speaking, you have to refer to classes in typescript using their constructor type. 一般来说,您必须使用构造函数类型引用typescript中的类。 However, as of typescript 2.8, there is a new InstanceType<T> type in the standard lib that can extract the instance type from the constructor type. 但是,从typescript 2.8开始,标准库中有一个新的InstanceType<T>类型,可以从构造函数类型中提取实例类型。 You can use that here to get the type safety you want. 您可以在此处使用它来获得所需的类型安全性。

For your snippet, you might add types like so: 对于您的代码段,您可以添加如下类型:

class Messages {
    getMessage<T extends {getMessage: () => string, new (...args: any[]): InstanceType<T>}>(classRef: T): string {
       // Here, classRef is properly inferred to have a `getMessage` method.
       return classRef.getMessage()
    }
}

class myClassA {
    constructor(public id: number, public message: string) {}
    static getMessage(): string {
        return 'hello from class A';
    }
}

class myClassB {
    constructor(public id: number, public message: string) {}
    static getMessage(): string {
        return 'hello from class B';
    }
}

var messages = new Messages();

// messageA and messageB inferred to have type: string
// You can change that back to any if you want.

// myClassA and myClassB both assignable as the argument to
// getMessage, so no problem there.
var messageA = messages.getMessage(myClassA);
var messageB = messages.getMessage(myClassB);

console.log(messageA) // 'hello from class A'
console.log(messageB) // 'hello from class B'

The line 这条线

getMessage<T extends {getMessage: () => string, new (...args: any[]): InstanceType<T>}>(classRef: T): string {

is where the type safety comes from. 是类型安全的来源。 That syntax says that whatever T is, it has to have a method getMessage (so if T is a class constructor, getMessage has to be a static method), and the new (...args: any[]): InstanceType<T> means that T has to be a class constructor. 该语法说无论T是什么,它都必须有一个方法getMessage (所以如果T是一个类构造函数, getMessage必须是一个静态方法),那么new (...args: any[]): InstanceType<T>表示T必须是类构造函数。

I've set the arguments for the constructor to be anything here, but if you know that the constructor will always take specific arguments, you could further narrow that. 我已经将构造函数的参数设置为此处的任何内容,但是如果您知道构造函数将始终采用特定参数,则可以进一步缩小它。 For your example, new (id: number, message: string): InstanceType<T> would work. 对于您的示例, new (id: number, message: string): InstanceType<T>将起作用。


Without typescript 2.8, you can't use InstanceType . 如果没有typescript 2.8,则无法使用InstanceType But you can still get type safety here by letting T represent the instance type, and then setting the type of the parameter to be a wrapped type that uses T as it's parameter. 但是,通过让T表示实例类型,然后将参数的类型设置为使用T作为其参数的包装类型,您仍然可以获得类型安全性。 So for your example: 所以对于你的例子:

interface Messageable<T> {
    new(...args: any[]): T;
    getMessage(): string;
}

class Messages {
    getMessage<T>(classRef: Messageable<T>): string {
       return classRef.getMessage()
    }
}

should work. 应该管用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM