考虑这个 failing example :
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;
}
在这里,我想限制装饰器中的匿名函数始终返回相关类的实例,但上面的方法不起作用,因为 T 实际上是 typeof Animal
而不是 Animal
.
在通用函数中,无论如何我可以得到类型 Animal
来自类型 typeof Animal
而不会像 function DecorateClass<TTypeOfClass, TClass>(...)
这样显式定义所有类型那样冗长烦人?
不幸的是,不支持在通用语法中使用 typeof,这是我试图让编译器理解我想要的东西的最佳选择:
function DecorateClass<T>(instantiate: (json:any) => T){
return (classTarget:typeof T) => { /*...*/ } // Cannot resolve symbol T
}
最佳答案
稍等片刻...
最近我需要一个函数的类型定义,它接受一个类作为参数,并返回该类的一个实例。当我想出一个 solution ,我很快就想到了这个问题。
基本上,使用可新类型可以在类与其实例之间建立关系,从而准确且完美地回答您的问题:
function DecorateClass<T>(instantiate: (...args: any[]) => T) {
return (classTarget: { new(...args: any[]): T }) => { /*...*/ }
}
解释
在 TypeScript 中,任何给定的新类型都可以使用以下签名定义:
new(...args: any[]): any
这类似于可接收或不接收参数并返回任何
(实例)的新类型(构造函数)。但是,没有说明返回的必须是 any
—— 它也可以是泛型类型。
并且由于我们在泛型类型参数中确切地知道了从构造函数返回的内容(通过类型推断应用装饰器的类),我们可以使用它来定义传入回调函数的返回类型。
我已经测试了装饰器,它似乎完全按照预期工作:
@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 无效.
编辑:继承
当涉及到继承时(例如:从实例化
返回一个派生类型),可分配性似乎被翻转了:你可以返回一个base类型,但是不是派生类型。
这是因为在泛型类型推断期间,instantiate
的返回类型优先于 classTarget
的“返回”类型。以下问题检查了这个确切的问题:
关于typescript - 使用类装饰器,我可以获得类类型实例的类型吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35373269/