typescript 带类参数的泛型类型推断

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24677592/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 02:55:11  来源:igfitidea点击:

Generic Type Inference with Class Argument

genericstypescript

提问by Antony Jones

I'm having an issue which defining a generic type based on a type I've passed in.

我有一个问题,根据我传入的类型定义泛型类型。

I have a piece of code witch “activates” a class, I can't get the type information from the type parameter so I am passing in class object (not an instance). However this breaks the Type inference.

我有一段代码女巫“激活”了一个类,我无法从类型参数中获取类型信息,所以我传入了类对象(不是实例)。然而,这打破了类型推断。

Here is a simplified example of what I'm trying to do:

这是我正在尝试做的一个简化示例:

interface IActivatable {
    id: number;
    name:string;
}

class ClassA implements IActivatable {
    public id: number;
    public name: string;
    public address:string;
}

class ClassB implements IActivatable {
    public id: number;
    public name: string;
    public age: number;
}

function activator<T extends IActivatable>(type:T): T {
    // do stuff to return new instance of T.
}

var classA:ClassA = activator(ClassA);

So far the only solution I've been able to come up with is to change the type of the typeargument to anyand manually set the generic type also (as shown below). However this seems long winded, is there another way to achieve this.

到目前为止,我能想出的唯一解决方案是将type参数的类型更改为any并手动设置泛型类型(如下所示)。然而,这似乎冗长,有没有另一种方法来实现这一点。

function activator<T extends IActivatable>(type:any): T {
    // do stuff to return new instance of T.
}

var classA:ClassA = activator<ClassA>(ClassA);

Thanks for any help you can give.

谢谢你提供的所有帮助。

采纳答案by Fenton

The type information in TypeScript is all erased during compilation, so you can't directly use any of the generic types, for example, at runtime.

TypeScript 中的类型信息在编译过程中都会被擦除,因此您不能直接使用任何泛型类型,例如,在运行时。

So here is what you can do...

所以这是你可以做的......

You can create classes by name by passing the name as a string. Yes; this involves waving your magic-string-wand. You also need to be aware of anything in your toolchain that may affect the names, for example any minifier that will crush the names (resulting in your magic string being out of sync):

您可以通过将名称作为字符串传递来按名称创建类。是的; 这涉及挥动你的魔杖。您还需要注意工具链中可能影响名称的任何内容,例如任何会破坏名称的压缩器(导致您的魔法字符串不同步):

class InstanceLoader<T> {
    constructor(private context: Object) {

    }

    getInstance(name: string, ...args: any[]) : T {
        var instance = Object.create(this.context[name].prototype);
        instance.constructor.apply(instance, args);
        return <T> instance;
    }
}

var loader = new InstanceLoader<IActivatable>(window);

var example = loader.getInstance('ClassA');

You can also get type names from instances at runtime, which I have shown in example format below taken from Obtaining TypeScript Class Names at Runtime:

您还可以在运行时从实例中获取类型名称,我在下面的示例格式中显示了这些名称,取自在运行时获取 TypeScript 类名称

class Describer {
    static getName(inputClass) { 
        var funcNameRegex = /function (.{1,})\(/;
        var results = (funcNameRegex).exec((<any> inputClass).constructor.toString());
        return (results && results.length > 1) ? results[1] : "";
    }
}

class Example {
}

class AnotherClass extends Example {
}

var x = new Example();
var y = new AnotherClass();

alert(Describer.getName(x)); // Example
alert(Describer.getName(y)); // AnotherClass

This would only be relevant if you wanted to generate "another of the same kind" as you could grab the type name and then use the object create stuff to get another.

只有当您想生成“另一个同类”时,这才有意义,因为您可以获取类型名称,然后使用对象创建内容来获取另一个。

回答by blorkfish

According to the language specification, you need to refer to the class type by it's constructor function. So instead of using type:T, use type: { new(): T;}as follows:

根据语言规范,您需要通过其构造函数来引用类类型。因此,不要使用type:T,而是使用type: { new(): T;}如下:

function activator<T extends IActivatable>(type: { new(): T ;} ): T {
    // do stuff to return new instance of T.
    return new type();
}

var classA: ClassA = activator(ClassA);

回答by Red Riding Hood

This is how the Typescript team recommends doing it:
Basically the same as blorkfish's answer, but extracted to an interface.

这就是 Typescript 团队建议这样做的方式:
基本上与 blokfish 的答案相同,但提取到一个界面。

interface IConstructor<T> {
    new (...args: any[]): T;

    // Or enforce default constructor
    // new (): T;
}

interface IActivatable {
    id: number;
    name: string;
}

class ClassA implements IActivatable {
    public id: number;
    public name: string;
    public address: string;
}

class ClassB implements IActivatable {
    public id: number;
    public name: string;
    public age: number;
}

function activator<T extends IActivatable>(type: IConstructor<T>): T {
    return new type();
}

const classA = activator(ClassA);

Source

来源

回答by Peter

Your problem is that ClassA is not actually of type T extends IActivatable. In fact, ClassA in the compiled javascript is actually a function that returns a constructor function.

您的问题是 ClassA 实际上不是 T 类型的扩展 IActivatable。其实编译出来的javascript中的ClassA其实就是一个返回构造函数的函数。