TypeScript中的通用类型参数推断优先级 [英] Generic type parameter inference priority in TypeScript

查看:233
本文介绍了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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆