Search code examples
typescriptdefinitelytyped.d.ts

How do you type a circular reference in a namespace in a declaration file?


I'm trying to type a UMD module (that I don't own so I can't change it) for the DefinitelyTyped project that has something like this (with actual functions of course):

function getModule() {
    function fn1() {}
    function fn2() {}

    fn1.fn2 = fn2;
    return fn1.fn1 = fn1;
}

if (typeof define === "function" && define.amd) {
    define(getModule());
} else if (typeof module !== "undefined" && module.exports) { 
    module.exports = getModule();
} else {
    this.moduleName = getModule();
}

Using the module-function.d.ts template, I have this so far:

export as namespace moduleName;

export = fn1;

declare function fn1(): void;

declare namespace fn1 {
    function fn2(): void;
}

I then used dts-gen to generate a .d.ts file for me to try to find out how to declare the fn1 function again in the fn1 namespace and got this:

/** Declaration file generated by dts-gen */

export = moduleName;

declare function moduleName(): void;

declare namespace moduleName {
    // Circular reference from moduleName
    const fn1: any;

    function fn2(): void;
}

However, when I tried combining the two in the following code I got the error 'fn1' is referenced directly or indirectly in its own type annotation..

export as namespace moduleName;

export = fn1;

declare function fn1(): void;

declare namespace fn1 {
    function fn2(): void;
    const fn1: any;
}

I don't want to do the following because then it wouldn't represent the fact that you can do require('moduleName').fn1.fn1.fn1.fn2 (or any number of fn1s.

export = moduleName;

declare function moduleName(): void;

declare namespace moduleName {
    function fn1(): void;
    function fn2(): void;
}

How would I type this module?


Solution

  • You can use an interface to accurately represent the recursive type:

    export = fn1;
    
    declare const fn1: Fn1
    
    interface Fn1 {
      // The call signature of fn1
      (): void
      // The circular reference
      fn1: Fn1
      // Other functions
      fn2: () => void
    }