【问题标题】:TypeScript: failure to resolve signature of generic class decoratorTypeScript:无法解析泛型类装饰器的签名
【发布时间】:2018-01-07 03:35:42
【问题描述】:

在这种情况下,我不明白为什么 TypeScript 需要为 Child2Child3 显式定义泛型类型:

abstract class Base {
    public static A: string = "Fallback_A";
    public DoSmthWithClassName(): string {
        return "Fallback_DoSmth";
    }

    constructor(...args: any[]); // overload for type consistency with children
    constructor(x: string)
    { }
}

// typeof any non-abstract child of Base
type BaseType = typeof Base & (new(...args: any[]) => Base);

// decorator, modifies methods and static properties
function ClassDecorator<T extends BaseType>(valueA: string): (value: T) => T {
    return (value: T) => {
        value.prototype.DoSmthWithClassName = () => value.name + ".DoSmth." + value.A;
        value.A = value.name + valueA;
        return value;
    }
}

@ClassDecorator("Foo") // OK
class Child0 extends Base {

}

@ClassDecorator("Foo") // OK
class Child1 extends Base {
    constructor(x: number) {
        super(x.toString());
    }
}

@ClassDecorator("Foo") // Unable to resolve...
class Child2 extends Base {
    static X: number = 0;
}

@ClassDecorator<typeof Child3>("Foo") // OK
class Child3 extends Base {
    static X: number = 0;
}

【问题讨论】:

  • 如果装饰器返回类型更改为 void,则 sn-p 运行没有错误,但编译器在每种情况下仍将泛型类型解析为 BaseType,我不明白为什么。

标签: typescript typescript-generics


【解决方案1】:

问题在于 TS 无法从唯一的参数 valueA 推断 T 的类型。你想要的是内部/返回函数的泛型参数:

// decorator, modifies methods and static properties
function ClassDecorator(valueA: string) {
    return function <T extends BaseType>(value: T): T {
        value.prototype.DoSmthWithClassName = () => value.name + ".DoSmth." + value.A;
        value.A = value.name + valueA;
        return value;
    }
}

您的版本不会导致 Child0 和 Child1 出现任何问题,因为它们在结构上与 base 相同。

【讨论】:

  • 我不明白你的想法,但是我决定放弃泛型并使用这种装饰器:function ClassDecorator(valueA: string): (value: typeof Base) =&gt; void,因为编译器假定在泛型情况下value.prototype.DoSmthWithClassName 类型是any
【解决方案2】:

现在回答太晚了,我不知道它是否仍然适合需要。但是,不需要在 ClassDecorator 中使用泛型参数,您仍然可以在其中实例化 Child2 并在其中使用“静态 X”并通过 Child2.X 访问 X。

这是因为没有构造函数的 ClassDecorator 就像没有船的有经验的水手一样,它需要一个 Type 以便 Typescript 可以确定将其编译为函数。

最好的解决方案是在 ClassDecorator 之后反映它,

const Reflected = (value:string) : ClassDecorator => {
  return reflected => { 
    // `reflected` here is `Type<Foo>`, not `Foo`
    console.log(`The reflected:  ${reflected}`)
  };
};

并且您可以立即使用它,因为 @Reflected("Foo") 不是一个新实例,

//@ClassDecorator("Foo") // Unable to resolve...
@Reflected("Foo")
class Child2 extends Base {
  public static X: number = 0;
  
}

我能想到的一些原因:

  • 类,基本上是运行时的 javascript 函数,过于通用,但常量实例不是(专注于类型),这就是为什么我们需要 ClassDecorator 来实现“依赖注入”和其他好处。 - 在 Typescript 中,我们需要一种“装饰”实例,而不是在设计时实例本身,它在编译时提供正确的类型。

TS Playground

PS:如果您使用的是“反射元数据”包,您可以通过console.log(Reflect.getMetadata('design:paramtypes', reflected)); 进一步了解 我没有看到任何副作用,如果我发现任何副作用,我会更新。

【讨论】:

    猜你喜欢
    • 2016-07-26
    • 2018-12-03
    • 2016-11-24
    • 2018-01-06
    • 2016-10-08
    • 1970-01-01
    • 2019-04-18
    • 2020-01-30
    • 2021-12-21
    相关资源
    最近更新 更多