首页 文章

TypeScript中的通用类型参数推断优先级

提问于
浏览
1

我有以下class decorator factory接受初始化函数作为其参数 . 在这个 initializer 函数中,我想返回一个对应于所涉及的类类型(或派生类型)的实例:

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装饰工厂

JsonObject 函数类似于具有泛型类型参数 T 的函数,接受回调函数返回 T 作为其参数,并返回接受返回 T 的新函数的函数 . 后者(返回的函数)显然是class decorator本身 .

编译器不允许我 - 例如 - 从这个 initializer 函数(或任何其他不匹配类型)返回一个字符串,这是应该的 .

子类型问题

但是,当使用子类型时,上述类型签名的行为完全相反:从 initializer 函数我可以返回基类型,但不是派生类型 - 在2步的中间类上使用时会发生以下错误继承模式:

@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'不能指定为'Kitty'类型 . 'Cat'类型中缺少属性'cutenessFactor' .

我相信我已经确定了错误的起源,它是由编译器在推断泛型时引起的:泛型类型参数 T 是从"T" _3000663中推断出来的,这意味着错误是由具有泛型类型 Kitty 的JsonObject函数引起的, Cat 显然不能分配给它,因此在这种情况下,类装饰器不能在 Cat 上使用 .

我想这样 T 是从"return"的"return"类型中推断出来的,这将解决我的问题 . 我怎么能做到这一点?

当然,当我明确指定泛型类型参数时,它可以完美地工作(但这会带来冗余信息):

@JsonObject<Cat>(function (json) {
    return new Kitty(); // OK, since type 'Kitty' is assignable to type 'Cat'
})
class Cat extends Animal { }

1 回答

  • 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;
    }
    

    如果你的意思是上面^它编译得很好

相关问题