使用类装饰器,我可以获得类类型实例的类型吗?

时间:2016-02-12 22:26:11

标签: typescript

考虑这个failing example

deltas.dt.total_seconds() / 3600.

2000-01-01   NaN
2000-01-02    24
2000-01-03    24
2000-01-04    24
2000-01-05    24
2000-01-06    24
2000-01-07    24
2000-01-08    24
2000-01-09    24
2000-01-10    24
Freq: D, dtype: float64

这里我想约束装饰器中的匿名函数,以便始终返回相关类的实例,但由于T实际上是function DecorateClass<T>(instantiate: (...params:any[]) => T){ return (classTarget:T) => { /*...*/ } } @DecorateClass((json:any) => { //Purely example logic here, the point is that it have to return //an instance of the class that the decorator runs on. var instance = new Animal(); instance.Name = json.name; instance.Sound = json.sound; return instance; }) class Animal { public Name:string; public Sound:string; } 而不是typeof Animal,因此上述方法不起作用。

在泛型函数中,无论如何,我可以从类型Animal获取类型Animal,而不会像明确定义typeof Animal之类的所有类型那样烦人地冗长吗?

不幸的是,不支持在通用语法中使用typeof,这是我试图让编译器理解我想要的东西的最佳选择:

function DecorateClass<TTypeOfClass, TClass>(...)

2 个答案:

答案 0 :(得分:4)

按住该行一秒......

最近我需要一个函数的类型定义,它接受一个类作为参数,并返回该类的实例。当我想出一个solution时,很快就会出现这个问题。

基本上,使用一个新类型可以让人联想到一个类与它的实例之间的关系,它可以准确而完美地回答你的问题:

function DecorateClass<T>(instantiate: (...args: any[]) => T) {
    return (classTarget: { new(...args: any[]): T }) => { /*...*/ }
}

说明

在TypeScript中,可以使用以下签名定义任何给定的新类型:

new(...args: any[]): any

这类似于一个新的类型(构造函数),它可能会也可能不会接受参数并返回any(实例)。但是,没有任何内容表明必须返回any - 它也可以是泛型类型。

由于我们在构造函数函数中返回的内容(通过类型推断应用装饰器的类)在泛型类型参数中,我们可以使用它来定义传入的回调函数的返回类型。 / p>

我测试了装饰器,它似乎正如预期的那样工作:

@DecorateClass((json: any) => {
    return new Animal(); // OK
})
@DecorateClass((json: any) => {
    return Animal; // Error
})
@DecorateClass((json: any) => {
    return "animal"; // Error
})
class Animal {
    public Name: string;
    public Sound: string;
}

这实际上使我以前的answer无效。

编辑:继承

当涉及继承时(例如:从instantiate返回派生类型),可分配性似乎被翻转:您可以返回 base 类型,但不能返回< em>派生类型。

这是因为instantiate中返回的类型优先于通用类型推断期间“返回”类型的classTarget。以下问题检查了这个确切的问题:

答案 1 :(得分:2)

修改

事实证明,你要求的是完全可能的。我添加了一个new answer,但也会留在这里,因为它可能包含对某人有价值的信息。这个答案表明了一个运行时解决方案,新的解决方案建议使用编译时解决方案。

我认为最好的选择是运行时类型检查,因为在装饰器函数中具有正确的类型:

function DecorateClass(instantiate: (...params: any[]) => any) {
    return (classTarget: Function) => {
        var instance = instantiate(/*...*/);

        if (!(instance instanceof classTarget)) {
            throw new TypeError();
        }

        // ...
    }
}

这不会产生编译时类型安全性。