How to merge several object methods declared using generics in TypeScript?












1















THE SCOPE:



Here is what I love about TypeScript:



interface CommandBus {
emit(type: 'execute', payload: { command: string }) : number;
emit(type: 'stop', payload: { pid: number }) : bool;
}


… and then, while I'm writing this commandBus.emit('stop', IntelliSense will tell me that the next function argument is payload: { pid: number }. This is priceless!



It is also possible to split it into several interfaces and TypeScript will merge it and the result will be the same:



interface CommandBus {
emit(type: 'execute', payload: { command: string }) : number;
}

interface CommandBus {
emit(type: 'stop', payload: { pid: number }) : bool;
}


This is what I use for my application. In different packages, I extend an interface with methods specific to that package. But the method signature is more complicated than the one above, and it has more common things, so I've created the generic:



interface IEmit<TType, TPayload> {
(type: TType, id: string, options: { payload: TPayload }) : void
}


And I've tried to use it with my interface:



interface CommandBus {
emit: IEmit<'execute', { command: string }>;
emit: IEmit<'stop', { pid: number }>;
}


THE PROBLEM: TypeScript cannot handle this syntax, it only applies the first emit declaration and ignoring others.



THE QUESTION: How do I manage to overload a method in an interface using function types or interfaces?










share|improve this question



























    1















    THE SCOPE:



    Here is what I love about TypeScript:



    interface CommandBus {
    emit(type: 'execute', payload: { command: string }) : number;
    emit(type: 'stop', payload: { pid: number }) : bool;
    }


    … and then, while I'm writing this commandBus.emit('stop', IntelliSense will tell me that the next function argument is payload: { pid: number }. This is priceless!



    It is also possible to split it into several interfaces and TypeScript will merge it and the result will be the same:



    interface CommandBus {
    emit(type: 'execute', payload: { command: string }) : number;
    }

    interface CommandBus {
    emit(type: 'stop', payload: { pid: number }) : bool;
    }


    This is what I use for my application. In different packages, I extend an interface with methods specific to that package. But the method signature is more complicated than the one above, and it has more common things, so I've created the generic:



    interface IEmit<TType, TPayload> {
    (type: TType, id: string, options: { payload: TPayload }) : void
    }


    And I've tried to use it with my interface:



    interface CommandBus {
    emit: IEmit<'execute', { command: string }>;
    emit: IEmit<'stop', { pid: number }>;
    }


    THE PROBLEM: TypeScript cannot handle this syntax, it only applies the first emit declaration and ignoring others.



    THE QUESTION: How do I manage to overload a method in an interface using function types or interfaces?










    share|improve this question

























      1












      1








      1








      THE SCOPE:



      Here is what I love about TypeScript:



      interface CommandBus {
      emit(type: 'execute', payload: { command: string }) : number;
      emit(type: 'stop', payload: { pid: number }) : bool;
      }


      … and then, while I'm writing this commandBus.emit('stop', IntelliSense will tell me that the next function argument is payload: { pid: number }. This is priceless!



      It is also possible to split it into several interfaces and TypeScript will merge it and the result will be the same:



      interface CommandBus {
      emit(type: 'execute', payload: { command: string }) : number;
      }

      interface CommandBus {
      emit(type: 'stop', payload: { pid: number }) : bool;
      }


      This is what I use for my application. In different packages, I extend an interface with methods specific to that package. But the method signature is more complicated than the one above, and it has more common things, so I've created the generic:



      interface IEmit<TType, TPayload> {
      (type: TType, id: string, options: { payload: TPayload }) : void
      }


      And I've tried to use it with my interface:



      interface CommandBus {
      emit: IEmit<'execute', { command: string }>;
      emit: IEmit<'stop', { pid: number }>;
      }


      THE PROBLEM: TypeScript cannot handle this syntax, it only applies the first emit declaration and ignoring others.



      THE QUESTION: How do I manage to overload a method in an interface using function types or interfaces?










      share|improve this question














      THE SCOPE:



      Here is what I love about TypeScript:



      interface CommandBus {
      emit(type: 'execute', payload: { command: string }) : number;
      emit(type: 'stop', payload: { pid: number }) : bool;
      }


      … and then, while I'm writing this commandBus.emit('stop', IntelliSense will tell me that the next function argument is payload: { pid: number }. This is priceless!



      It is also possible to split it into several interfaces and TypeScript will merge it and the result will be the same:



      interface CommandBus {
      emit(type: 'execute', payload: { command: string }) : number;
      }

      interface CommandBus {
      emit(type: 'stop', payload: { pid: number }) : bool;
      }


      This is what I use for my application. In different packages, I extend an interface with methods specific to that package. But the method signature is more complicated than the one above, and it has more common things, so I've created the generic:



      interface IEmit<TType, TPayload> {
      (type: TType, id: string, options: { payload: TPayload }) : void
      }


      And I've tried to use it with my interface:



      interface CommandBus {
      emit: IEmit<'execute', { command: string }>;
      emit: IEmit<'stop', { pid: number }>;
      }


      THE PROBLEM: TypeScript cannot handle this syntax, it only applies the first emit declaration and ignoring others.



      THE QUESTION: How do I manage to overload a method in an interface using function types or interfaces?







      typescript






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 21 '18 at 11:56









      termosatermosa

      15618




      15618
























          1 Answer
          1






          active

          oldest

          votes


















          1














          Declaration merging can't change the type of an existing field (this is a design limitation). An alternate solution would be to declare a type for the emit field and extend that:



          interface IEmit<TType, TPayload> {
          (type: TType, id: string, options: { payload: TPayload }) : void
          }

          interface CommandBus {
          emit: CommandBusEmit;
          }

          //default type for the emit field
          interface CommandBusEmit { }

          //extensions to it
          interface CommandBusEmit extends IEmit<'execute', { command: string }> { }
          interface CommandBusEmit extends IEmit<'stop', { pid: number }> { }

          declare let cb: CommandBus;
          cb.emit('execute', "", { payload : { command: ""}})
          cb.emit('stop', "", { payload: { command: "" } }) // error
          cb.emit('stop', "", { payload : { pid: 1}}) // ok





          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53411529%2fhow-to-merge-several-object-methods-declared-using-generics-in-typescript%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            1














            Declaration merging can't change the type of an existing field (this is a design limitation). An alternate solution would be to declare a type for the emit field and extend that:



            interface IEmit<TType, TPayload> {
            (type: TType, id: string, options: { payload: TPayload }) : void
            }

            interface CommandBus {
            emit: CommandBusEmit;
            }

            //default type for the emit field
            interface CommandBusEmit { }

            //extensions to it
            interface CommandBusEmit extends IEmit<'execute', { command: string }> { }
            interface CommandBusEmit extends IEmit<'stop', { pid: number }> { }

            declare let cb: CommandBus;
            cb.emit('execute', "", { payload : { command: ""}})
            cb.emit('stop', "", { payload: { command: "" } }) // error
            cb.emit('stop', "", { payload : { pid: 1}}) // ok





            share|improve this answer




























              1














              Declaration merging can't change the type of an existing field (this is a design limitation). An alternate solution would be to declare a type for the emit field and extend that:



              interface IEmit<TType, TPayload> {
              (type: TType, id: string, options: { payload: TPayload }) : void
              }

              interface CommandBus {
              emit: CommandBusEmit;
              }

              //default type for the emit field
              interface CommandBusEmit { }

              //extensions to it
              interface CommandBusEmit extends IEmit<'execute', { command: string }> { }
              interface CommandBusEmit extends IEmit<'stop', { pid: number }> { }

              declare let cb: CommandBus;
              cb.emit('execute', "", { payload : { command: ""}})
              cb.emit('stop', "", { payload: { command: "" } }) // error
              cb.emit('stop', "", { payload : { pid: 1}}) // ok





              share|improve this answer


























                1












                1








                1







                Declaration merging can't change the type of an existing field (this is a design limitation). An alternate solution would be to declare a type for the emit field and extend that:



                interface IEmit<TType, TPayload> {
                (type: TType, id: string, options: { payload: TPayload }) : void
                }

                interface CommandBus {
                emit: CommandBusEmit;
                }

                //default type for the emit field
                interface CommandBusEmit { }

                //extensions to it
                interface CommandBusEmit extends IEmit<'execute', { command: string }> { }
                interface CommandBusEmit extends IEmit<'stop', { pid: number }> { }

                declare let cb: CommandBus;
                cb.emit('execute', "", { payload : { command: ""}})
                cb.emit('stop', "", { payload: { command: "" } }) // error
                cb.emit('stop', "", { payload : { pid: 1}}) // ok





                share|improve this answer













                Declaration merging can't change the type of an existing field (this is a design limitation). An alternate solution would be to declare a type for the emit field and extend that:



                interface IEmit<TType, TPayload> {
                (type: TType, id: string, options: { payload: TPayload }) : void
                }

                interface CommandBus {
                emit: CommandBusEmit;
                }

                //default type for the emit field
                interface CommandBusEmit { }

                //extensions to it
                interface CommandBusEmit extends IEmit<'execute', { command: string }> { }
                interface CommandBusEmit extends IEmit<'stop', { pid: number }> { }

                declare let cb: CommandBus;
                cb.emit('execute', "", { payload : { command: ""}})
                cb.emit('stop', "", { payload: { command: "" } }) // error
                cb.emit('stop', "", { payload : { pid: 1}}) // ok






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 21 '18 at 12:08









                Titian Cernicova-DragomirTitian Cernicova-Dragomir

                70k34866




                70k34866
































                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53411529%2fhow-to-merge-several-object-methods-declared-using-generics-in-typescript%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    mysqli_query(): Empty query in /home/lucindabrummitt/public_html/blog/wp-includes/wp-db.php on line 1924

                    How to change which sound is reproduced for terminal bell?

                    Can I use Tabulator js library in my java Spring + Thymeleaf project?