Search code examples
typescriptoverloadingtypescript-typingshyperscript

TypeScript function overloading for hyperscript signature


I'm currently dabbling around with more advanced typings in typescript and was wondering how one would define a function like the one from hyperscript. I tried various approaches but I'm not able to successfully overload the h function and make all CallExpressions listed under the usage comment to pass.

Here is what I have so far:

interface IProps {
  [key: string]: any;
}

function h(tag: string, props?: IProps): void;
function h(tag: string, children: string): void; // <- marked as invalid
function h(tag: string, props: IProps, children?: string): void {
  // ...code goes here
}

Usage:

h("div");
h("div", "Hello World");
h("div", { className: "test" });
h("div", { className: "test" }, "Hello World"); // <- marked as invalid

Solution

  • I found the answer after waking up this morning. In TypeScript the implementation signature is not visible to the outside world and thus the calling expression must match one of the overloads.

    Additionally, the types in the implementation signature must catch all previous overloads.

    // Overloads, must match one of these
    function h(tag: string, children?: string): void;
    function h(tag: string, props: IProps, children?: string): void;
    
    // Not visible for the outside world
    function h(tag: string, props: IProps | string, children?: string): void {
      // ...
    }