2016-04-25 80 views
1

我有以下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 { } 

回答

0

您的意思是链上的装饰,还是你的意思是这样的:

function JsonObject<T>(initializer: (json: any) => T) { 
    return function (target: { new (...args: any[]): T }) { 
     return null; 
    } 
} 


@JsonObject(function (json) { 
    return new Foo(); 
}) 
class Foo { 
    foo: string; 
} 

@JsonObject(function (json) { 
    // Test case: return an exact corresponding type. 
    return new Bar(); // OK, as it should be 
}) 
class Bar extends Foo { 
    bar: string; 
} 

@JsonObject(function (json) { 
    // Test case: return a derived type. 
    return new Baz(); // Ok 
}) 
class Baz extends Bar { 
    baz: string; 
} 

如果你的意思是上面的^它编译罚款

+0

是的,我的意思是单独使用,但关于类的类型我相信打字稿编译器无法识别类的装饰。除非我错过了一些东西,否则它应该没什么关系。问题是'JsonObject'中泛型类型参数'T'是从'initializer'函数返回的内容推断出来的,而不是'target'构造函数返回的内容。 –

+0

这个编译得很好。不应该吗? – basarat

+0

确实如此。但是,如果您要在应用于“Foo”的修饰器中返回“Bar”的实例,则会出现错误。这是由于编译器如何根据返回的JSONObject的泛型类型来推断的。 –