您可能希望您的装饰器工厂是非泛型的,并让它生成一个泛型装饰器。现在你的装饰器工厂是通用的并且返回一个非通用的装饰器。这就是两者的区别:
// bad
declare const genericFactoryForSpecificFunction: <T>() => (x: T) => T;
const oops = genericFactoryForSpecificFunction()(123); // unknown
// good
declare const specificFactoryForGenericFunction: () => <T>(x: T) => T;
const okay = specificFactoryForGenericFunction()(123); // 123
在不好的情况下,当您调用 genericFactoryForSpecificFunction() 时,编译器必须推断出 T,但在该表达式中没有 T 类型的值可供它参考。所以它选择unknown,这意味着对参数123 的后续调用返回unknown。在好的情况下,编译器会将T 的推断推迟到对specificFactoryForGenericFunction() 返回的函数的调用。这让编译器实际上使用T 类型的值来推断T,因此你得到123。
进行此更改后,您将需要您的 Decorator 类型是表示通用函数调用签名的特定类型。我想可以将您的条件类型转换为该形式,但它有点混乱并且使用了其余元组:
type PossibleDecorator = <T>(
target: T,
...args: T extends Function ? [] : [string?]
) => T extends Function ? T : void;
declare const pd: PossibleDecorator;
pd(RegExp); // okay, class decorator
pd({a: ""}, "a") // okay, prop decorator
pd(RegExp, "oops"); // error, tried to pass prop to class decorator
我会说多重调用签名重载会更好,并且具有相同的行为:
type Decorator = {
<T extends new (...args: any) => any>(ctor: T): T; // class decorator
<T, K extends keyof T>(proto: T, member: K): void; // prop decorator
}
declare const d: Decorator;
d(RegExp); // okay, class decorator
d({ a: "" }, "a") // okay, prop decorator
d(RegExp, "oops"); // error, tried to pass prop to class decorator
所以,你的示例代码会变成这样:
function Schema(): Decorator {
return (target: any, propertyKey?: string) => {
if (target instanceof Function) {
return target;
}
return;
};
}
@Schema()
export default class MyClass {
@Schema()
bool: boolean = false;
}
看起来不错。
好的,希望对您有所帮助;祝你好运!
Playground link to code