I want to create a field decorator that optionally can take an argument. The argument should contain any of the following values: nothing, a boolean or a function. I know how to do this, but I'm not 100% happy with the result:
export class TestClass{
@Required(isRequired)
public testField: string;
}
export function isRequired():boolean{
... some validation logic, maybe depending on other fields...
return result;
}
Implementation of @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
}
}
So this works fine, but when I do not want to add an expression (and thus use the defaulted "true" expression) I want to be able to write it like this:
class TestClass{
@Required
public testField: string;
}
I get a TypeScript error (TS1240) saying:
Unable to resolve signature of property decorator when called as an expression. Supplied parameters do not match any signature of call target
So I need to write @Required()
class TestClass{
@Required()
public testField: string;
}
Is it possible to write a decorator implementation that takes optionally an argument and when that argument is not specified there is no need to add the "()" ?
Actually, it is possible.
Here is a working example:
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 { }
The most important part is the following two lines:
export function Component(target: Target): void;
export function Component(name: string): (target: Target) => void;
If anyone is looking for more information, check out this GitHub issue .
No, you probably can't do that.
The reason for that is that decorators have a specific signature (which varies depending on the type of the decorator).
If you use a decorator function then you don't need the brackets, but if you use a decorator factory (like you do in your example) then you must call it using brackets.
What you can do is separating the two into two different functions:
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);
}
}
And then you can either:
class TestClass {
@Required
public testField: string;
}
Or:
class TestClass2 {
@RequiredWith(true)
public testField: string;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.