Search code examples
typescripttypescript-declarations

Reference module when overloading a global function in Typescript


I'm using moment.js (specifically, moment-timezone) which has an interface called Duration. Duration.prototype.valueOf() returns a number, so in JavaScript, calling

setInterval(myCallback, moment.duration(30, 'seconds'));

works just fine.

I want to write a TypeScript declaration file that allows this.

global.d.ts

export {};

declare global {
    function setTimeout(callback: (...args: any[]) => void, ms: Duration, ...args: any[]): NodeJS.Timeout;

    function setInterval(callback: (...args: any[]) => void, ms: Duration, ...args: any[]): NodeJS.Timeout;
}

When I prepend

import { Duration } from 'moment-timezone';

it treats the .d.ts file as a module declaration, and so it doesn't affect the global namespace.

I thought to move the import inside the declare global scope, but it still treated Duration as any.

I've also tried

/// <reference path="node_modules/@types/moment-timezone/index.d.ts" />

but that doesn't seem to do anything.

I've seen a few answers that mentioned something about a setting in tsconfig.json, but that's not an option for me and this really seems like something that should just be possible in the first place.


Solution

  • This requires two steps:

    1. Declare the module outside the declare global scope.
    2. Place imports inside the declare global scope.

    For the OP example:

    export {}
    
    declare module 'moment-timezone';
    
    declare global {
        import { Duration } from 'moment-timezone';
    
        function setTimeout(callback: (...args: any[]) => void, ms: Duration, ...args: any[]): NodeJS.Timeout;
    
        function setInterval(callback: (...args: any[]) => void, ms: Duration, ...args: any[]): NodeJS.Timeout;
    }
    

    If you want to import your own types into an external module, place the import inside the declare module scope, and make sure your types are within a declare module scope of their own.

    typings/my-custom-types.d.ts

    declare module 'my-custom-types' { // <-- this was the missing line that was giving me trouble
        export interface MyStringInterface {
            valueOf(): string;
        }
    }
    

    typings/some-lib/index.d.ts

    declare module 'some-lib' {
        import { MyStringInterface } from 'my-custom-types';
    
        export interface SomeExistingClass {
            // Add your own signatures
            someExistingMethod(stringParam: MyStringInterface): any;
        }
    }