Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript - inter-dependency decorators

Tags:

typescript

From the TypeScript docs

As such, the following steps are performed when evaluating multiple decorators on a single declaration in TypeScript:

  1. The expressions for each decorator are evaluated top-to-bottom. The
  2. results are then called as functions from bottom-to-top.

Problem

I have two decorators where one decorator depends on another:

Example

@test()
@testCase({...})
public doSomething(): void {
}

@testCase() depends on @test() because the test need to be added to the test runner, before the test cases can be added to the test.

I could do...

@testCase({...})
@test()
public doSomething(): void {
}

but it seems odd to declare test cases before declaring that something is a test.

Is there any way to have decorators that depend on each other, i.e. a testCase must come after a test?

like image 891
Matthew Layton Avatar asked Mar 27 '26 23:03

Matthew Layton


1 Answers

You can add extra information to the target function in testcase and then check for the extra information in the test decorator. Below the extra information is a callback to which more decorators can contribute to. This offers a lot of flexibility, but you could add other information instead of a callback and use that information in the test decorator.

interface IAfterTestRegistered {
    afterTestRegisteredCallback?: ()=> void
}
// We could potentially have more then one contributor that need a callback executed.
function addCallback(obj: IAfterTestRegistered, callback : () => void) {
    let oldCallback = obj.afterTestRegisteredCallback;
    if(oldCallback != undefined) {
        obj.afterTestRegisteredCallback = () =>{
            oldCallback();
            callback();
        }
    }
    else{
        obj.afterTestRegisteredCallback = callback;
    }
}

function test() : MethodDecorator {
    return function<T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) : void {
        console.log("test");
        let targetFunction: IAfterTestRegistered = target[propertyKey];
        if(targetFunction.afterTestRegisteredCallback) {
            targetFunction.afterTestRegisteredCallback();
        }
    }
}

function testCase(data: { index: number}) : MethodDecorator {
    return function<T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) : void {
        console.log(`testcase ${data.index}`);
        addCallback(target[propertyKey], () =>
        {
            console.log(`after-register testcase ${data.index}`);
        });
    }
}

class myClass {
    @test()
    @testCase({ index: 1 })
    @testCase({ index: 2 })
    public doSomething(): void {
    }
}
// Will output
// testcase 2
// testcase 1
// test
// after-register testcase 2
// after-register testcase 1
like image 117
Titian Cernicova-Dragomir Avatar answered Mar 31 '26 09:03

Titian Cernicova-Dragomir



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!