我有以下class decoratorfactory接受初始化函数作为其参数。在这种initializer
功能我想返回对应于所涉及的类型(或派生一个)的实例:TypeScript中的通用类型参数推理优先级
function JsonObject<T>(initializer: (json: any) => T) {
return function (target: { new (...args: any[]): T }) {
// ...
}
}
@JsonObject(function (json) {
return new Animal();
})
class Animal {
name: string;
}
返回的确切类的实例(如上面)工作正常,但是...
短版
返回一个派生类的实例确实不。我可以返回一个基地实例,但不是派生的之一。我不能,例如,返回Cat
:
@JsonObject(function (json) {
return new Cat(); // Error.
})
class Animal{
name: string;
}
class Cat extends Animal {
color: string;
}
...即使猫为动物。然而,我可以返回一个动物而不是猫(这是错误的,如动物不一定是猫),对于猫:
@JsonObject(function (json) {
return new Animal(); // OK, but it shouldn't be
})
class Cat extends Animal {
color: string;
}
龙版
的的JSONObject Decorator工厂
JsonObject
功能类似于函数与一般类型参数T
,接受回调函数返回T
作为它的参数,并返回一个接受新类型的函数返回一个T
。后者(返回的函数)显然是class decorator本身。
编译器不允许我 - 例如 - 从这个initializer
函数(或任何其他不匹配类型)返回一个字符串,这是应该的。
问题的亚型
然而,上述类型的签名在完全相反的方式行为时亚型被使用:从initializer
函数I可以返回一个基型,但不是一个派生型 -
@JsonObject(function (json) {
// Test case: return a base type.
return new Animal(); // OK, but it shouldn't be: an 'Animal' is not a 'Cat'
})
@JsonObject(function (json) {
// Test case: return an exact corresponding type.
return new Cat(); // OK, as it should be
})
@JsonObject(function (json) {
// Test case: return a derived type.
return new Kitty(); // <-- Error, but it should be OK, a Kitty *is* a Cat
})
class Cat extends Animal {
color: string;
}
class Kitty extends Cat {
cutenessFactor: number;
}
Error: Type 'Cat' is not assignable to type 'Kitty'. Property 'cutenessFactor' is missing in type 'Cat'.
我相信我已经精确定位的由来:上中产阶级的2步遗传模式的使用时,会出现以下错误的错误,它是由编译器在推断泛型时引起的:通用类型参数T
从“T” in initializer: (json: any) => T
推断,这意味着错误是由具有通用类型Kitty
的JsonObject函数引起的,其中Cat
显然不能这样分配,因此在这种情况下,类装饰器不能在Cat
上使用。
我希望T
可以从target
的“返回”类型推断出来,这可以解决我的问题。我怎么能做到这一点?
当然,当我明确指定的泛型类型参数,它完美的作品(但这种带有冗余信息):
@JsonObject<Cat>(function (json) {
return new Kitty(); // OK, since type 'Kitty' is assignable to type 'Cat'
})
class Cat extends Animal { }
是的,我的意思是单独使用,但关于类的类型我相信打字稿编译器无法识别类的装饰。除非我错过了一些东西,否则它应该没什么关系。问题是'JsonObject'中泛型类型参数'T'是从'initializer'函数返回的内容推断出来的,而不是'target'构造函数返回的内容。 –
这个编译得很好。不应该吗? – basarat
确实如此。但是,如果您要在应用于“Foo”的修饰器中返回“Bar”的实例,则会出现错误。这是由于编译器如何根据返回的JSONObject的泛型类型来推断的。 –