Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to infer receiver payload types #786

Open
Andreas-Hjortland opened this issue Jan 13, 2023 · 1 comment
Open

Add support to infer receiver payload types #786

Andreas-Hjortland opened this issue Jan 13, 2023 · 1 comment

Comments

@Andreas-Hjortland
Copy link

Andreas-Hjortland commented Jan 13, 2023

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Performance issue
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => https://github.com/ngxs/store/blob/master/CONTRIBUTING.md
[ ] Other... Please describe:

Current behavior

I would love some functionality that enables the typescript compiler to ensure that the payload parameters matches the receiver function and the emitters both when using @Emitter(...) decorator and when I am using the EmitterService.action(...) method. Now we have to manually ensure that the payload type of the receiver and that we use for the emitters matches or we get a runtime error.

Expected behavior

We could introduce some utility types that extract the payload type based on the receiver method like into the project. I have created the following types that I am using in a project I'm working on that might work:

type EmitterFunctionBase = (ctx: any, action: EmitterAction<any>) => any;
type EmittableParams<T extends EmitterFunctionBase> =
    T extends (ctx: any, action: infer TAction) => any ? (
        TAction extends EmitterAction<infer TPayload> ? TPayload : void
    ) : never;
declare type EmittableFunction<T extends EmitterFunctionBase, U = any> = Emittable<EmittableParams<T>, U>;

If we introduced those types. we could also add the following overload to the EmitterService.action method to get type inference when using the EmitterService directly:

export class EmitterService {
    // ...
    action<T extends EmitterFunctionBase, U = any>(receiver: T): Emittable<EmittableParams<T>, U>;
    action<T = void, U = any>(receiver: Function): Emittable<T, U> {
        // ...
    }
    // ...
}

We would be able to use receivers without explicitly typing the payload type where we are using them. Here is an example of a consumer of the receive would look with these proposed types:

export class MyState {
    @Receiver()
    static doStuff(ctx: StateContext<MyState>, action: EmitterAction<number>) {
        ctx.patchState({
            count: ctx.getState().count + action.payload
        });
    }
}

export class MyComponent {
    @Emitter(MyState.doStuff)
    increment: EmittableFunction<typeof MyState.doStuff>;

    constructor(emitter: EmitterService) {
        emitter.action(MyState.doStuff).emit(5); // The payload type is automatically inferred because we match the `EmitterFunctionBase` overload
        // emitter.action(MyState.doStuff).emit({ foo: true }); // This causes an compiler error
    }

   click($event) {
       this.increment.emit(5);
       // this.increment.emit({foo: true}); // This also causes an compiler error
   }
}

What is the motivation / use case for changing the behavior?

This would make refactoring easier since the compiler can catch type errors when we change the payload type of a receiver without us having to manually search and update all the types every place the receiver is referenced

Others:
The extra overload of the EmitterService.action method might cause breaking changes if someone uses a payload type matching the EmitterFunctionBase type because then typescript would match and extract the parameters instead of using the type as is, but otherwise I think this would just be an improvement of the library ergonomics if the users opt into it.

If you would like to, I can create a pull request where I introduce the types and overload into the project

@Andreas-Hjortland
Copy link
Author

Andreas-Hjortland commented Jan 16, 2023

I created a repository at https://github.com/Andreas-Hjortland/ngxs-emitter-types where I added the type enhancements so that you can see how it works.. If you change the signature of any of the receivers (for instance, change the removePost payload to be the id instead of a whole post, you will see that we get compiler errors without having to find all references to the removePost and updating the types there)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant