TypeScript中的通用类型参数推断优先级 [英] Generic type parameter inference priority in TypeScript
问题描述
我有以下类装饰器 工厂接受初始化函数作为其论点。在此 initializer
函数中,我想返回一个与所涉及的类类型(或派生类类型)相对应的实例:
I have the following class decorator factory accepting an initializer function as its argument. In this initializer
function I would like to return an instance corresponding to the involved class type (or that of a derived one):
function JsonObject<T>(initializer: (json: any) => T) {
return function (target: { new (...args: any[]): T }) {
// ...
}
}
@JsonObject(function (json) {
return new Animal();
})
class Animal {
name: string;
}
返回精确类的实例(如上所述)可以正常工作,但是。 ..
Returning an instance of the exact class (as above) works correctly, but...
返回派生类的实例不会不是 。我可以返回一个 base 实例,但不能返回一个 derived 实例。例如,我无法返回 Cat
:
Returning an instance of a derived class does not. I can return a base instance, but not a derived one. I can't, for example, return a Cat
:
@JsonObject(function (json) {
return new Cat(); // Error.
})
class Animal{
name: string;
}
class Cat extends Animal {
color: string;
}
...即使猫是动物。但是,我可以为猫返回 Animal 而不是Cat(这是错误的,因为Animal不一定是Cat):
... even though a Cat is an Animal. I can however, return an Animal instead of a Cat (which is wrong, as an Animal is not necessarily a Cat), for a Cat:
@JsonObject(function (json) {
return new Animal(); // OK, but it shouldn't be
})
class Cat extends Animal {
color: string;
}
长版
JsonObject装饰器工厂
JsonObject
函数类似于具有泛型类型参数 T
的函数,接受返回 T
作为其参数的回调函数,并且返回接受新类型的函数,返回 T
。后者(返回的函数)是类装饰器本身。
Long Version
The JsonObject Decorator Factory
The JsonObject
function is analogous to a function with a generic type parameter T
, accepting a callback function returning a T
as its argument, and returning a function accepting a newable type returning a T
. The latter (the returned function) is the class decorator itself, obviously.
编译器不允许我-例如-从此 initializer <返回字符串/ code>函数(或其他任何不匹配的类型)。
The compiler won't allow me to -- for example -- return a string from this initializer
function (or any other mismatching type), which is as it should be.
但是,当使用子类型时,上述类型签名的行为恰好相反:从 initializer
函数中,我可以返回 base 类型,但不是派生的类型-在两步继承模式的中间类上使用时,会发生以下错误:
However, the above type signature behaves in the exact opposite way when subtypes are used: from the initializer
function I can return a base type, but not a derived type -- the following error occurs when used on the middle class of a 2-step inheritance pattern:
@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;
}
错误:类型 Cat不可分配键入凯蒂。属性 cutenessFactor在猫类型中缺失。
Error: Type 'Cat' is not assignable to type 'Kitty'. Property 'cutenessFactor' is missing in type 'Cat'.
我相信我已经查明了错误的起因,这是由于由编译器在推断泛型时:泛型类型参数 T
是从 initializer中的 T 推断的:(json :any)=> T
,这意味着错误是由具有普通类型 Kitty
的JsonObject函数引起的,而 Cat
显然不能这样分配,因此在这种情况下不能在 Cat
上使用类装饰器。
I believe I've pinpointed the origin of the error, it is caused by the compiler when inferring generics: the generic type parameter T
is inferred from the "T" in initializer: (json: any) => T
, which means the error is caused by the JsonObject function having the generic type Kitty
, to which Cat
is obviously not assignable as such, and therefore the class decorator cannot be used on Cat
in this case.
我希望这样,以便从 target
的返回类型推断出 T
解决我的问题。我怎么能做到这一点?
I would like it so that T
is instead inferred from the "return" type of target
, which would solve my problem. How could I accomplish this?
当然,当我明确指定泛型类型参数时,它可以正常工作(但这带有多余的信息):
Of course, when I explicitly specify the generic type parameter, it works flawlessly (but this carries redundant information):
@JsonObject<Cat>(function (json) {
return new Kitty(); // OK, since type 'Kitty' is assignable to type 'Cat'
})
class Cat extends Animal { }
推荐答案
您是要链接装饰器,还是您要这样做:
Did you mean to chain the decorators or did you mean this:
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;
}
如果您的意思是上述^,则可以很好地编译
If you meant the above ^ it compiles fine
这篇关于TypeScript中的通用类型参数推断优先级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!