Loupax
Loupax

Reputation: 4914

Cannot add module declarations to node package that exports function

I started using Typescript recently and I came across the need to require an npm module inside my application. Since said npm module has no type definitions of it's own, I also decided to fork it and add my own. How hard can it be?

Here is the npm module I installed on my project:

/**
 * Given a number, return a zero-filled string.
 * From http://stackoverflow.com/questions/1267283/
 * @param  {number} width
 * @param  {number} number
 * @return {string}
 */
module.exports = function zeroFill (width, number, pad) {
  if (number === undefined) {
    return function (number, pad) {
      return zeroFill(width, number, pad)
    }
  }
  if (pad === undefined) pad = '0'
  width -= number.toString().length
  if (width > 0) return new Array(width + (/\./.test(number) ? 2 : 1)).join(pad) + number
  return number + ''
}

Simple enough, it just exports a single function. Now let's see how to get typescript work with it...

Attempt #1:

Definition

declare module "zero-fill"{
     export function zeroFill(width:number, num:number, pad?:string|number):string
     export function zeroFill(width:number):{(num:number, pad?:string|number):string}
}

Source code

import * as zeroFill from "zero-fill";
console.log(zeroFill(10, 10));

Generated code

"use strict";
exports.__esModule = true;
var zeroFill = require("zero-fill");
console.log(zeroFill(10, 10));

This one generates code that works, but at the same time gives an error. Also no autocomplete from my IDE.

Cannot invoke an expression whose type lacks a call signature. Type 'typeof "zero-fill"' has no compatible call signatures.

Atempt #2

Definition

declare module "zero-fill"{
     // Notice the default keywords
     export default function zeroFill(width:number, num:number, pad?:string|number):string
     export default function zeroFill(width:number):{(num:number, pad?:string|number):string}
}

Source

import zeroFill from "zero-fill";
console.log(zeroFill(10, 10));

Generated

"use strict";
exports.__esModule = true;
var zero_fill_1 = require("zero-fill");
console.log(zero_fill_1["default"](10, 10));

Here I prefer the syntax I use inside my typescript and the compiler seems to like it as well. Zero compiler errors and also type hinting works on IDEA. Too bad the generated code gives me an TypeError: zero_fill_1.default is not a function error when run...

Attempt #3

Definition

declare module "zero-fill"{
    function zeroFill(width:number, num:number, pad?:string|number):string
    function zeroFill(width:number):{(num:number, pad?:string|number):string}
    export {zeroFill};
}

Source

import {zeroFill} from "zero-fill";
console.log(zeroFill(10, 10));

Generated

"use strict";
exports.__esModule = true;
var zero_fill_1 = require("zero-fill");
console.log(zero_fill_1.zeroFill(10, 10));

Exactly the same as before... The compiler and IDE like this but the runtime doesn't

I could go on but I believe you get the idea. Is it possible to make this npm module usable inside typescript without making changes to it's actual code? What am I doing wrong and how can I import this function properly?

Upvotes: 1

Views: 67

Answers (1)

alexanderbird
alexanderbird

Reputation: 4198

I believe you're looking for this documentation

declare module 'zero-fill' {
    function zeroFill() /* etc. */
    export = zeroFill;

    /* for any additional types */
    namespace zeroFill {
        interface FooBar { /* ... */ }
    }
}

then import it as:

import zeroFill = require('zero-fill');

Upvotes: 2

Related Questions