如何向 TypeScript 中的装饰器添加可选参数?

How to add an optional parameter to a decorator in TypeScript?

我想创建一个字段装饰器,它可以选择性地接受一个参数。 该参数应包含以下任何值:无、布尔值或函数。 我知道该怎么做,但我对结果不是 100% 满意:

export class TestClass{

   @Required(isRequired) 
   public testField: string;

}

export function isRequired():boolean{
   ... some validation logic, maybe depending on other fields...
   return result;
}

@Required 的实现:

export function Required(expression?: boolean|Function): Function {
    return (target: any, key: string) => {
        if (expression === null || typeof expression == 'undefined') {
            expression = true;
        }
        console.log("Required found: " + expression, ":", target, key);
        ... register the field and its validation expression for later usage
    }
}

所以这很好用,但是当我不想添加表达式时(因此使用默认的 "true" 表达式)我希望能够这样写:

class TestClass{

   @Required
   public testField: string;

}

我收到 TypeScript 错误 (TS1240) 说:

Unable to resolve signature of property decorator when called as an expression. Supplied parameters do not match any signature of call target

所以我需要写@Required()

class TestClass{

   @Required()
   public testField: string;

}

是否可以编写一个带有可选参数的装饰器实现,并且当未指定该参数时无需添加“()”?

不,你可能做不到。
原因是装饰器具有特定的签名(根据装饰器的类型而有所不同)。
如果您使用装饰器函数,则不需要括号,但如果您使用 a decorator factory(就像您在示例中所做的那样),则必须使用括号调用它。

你可以做的是将两者分成两个不同的函数:

function Required(target: any, key: string, expression?: boolean | Function) {
    if (expression === null || typeof expression == 'undefined') {
        expression = true;
    }
    console.log("Required found: " + expression, ":", target, key);
}

function RequiredWith(expression: boolean | Function): Function {
    return (target: any, key: string) => {
        return Required(target, key, expression);
    }
}

然后你可以:

class TestClass {
    @Required
    public testField: string;
}

或:

class TestClass2 {
    @RequiredWith(true)
    public testField: string;
}

(code in playground)

其实是可以的

这是一个工作示例:

export type Target = {
  new (...args: any[]): any,
  name: string
};

export function Component(target: Target): void;
export function Component(name: string): (target: Target) => void;
export function Component(nameOrTarget: string | Target) {
  if (typeof nameOrTarget !== 'string') {
    console.log(nameOrTarget.name, ' is now decorated');
  } else {
    return function (target: Target) {
      const name = nameOrTarget || target.name;
      console.log(name, ' is now decorated');
    };
  }
}

@Component
export class MyDatabase { }

@Component('Hello Db')
export class MyHelloDatabase { }

最重要的部分是下面两行:

export function Component(target: Target): void;
export function Component(name: string): (target: Target) => void;

如果有人正在寻找更多信息,请查看此 GitHub issue