1

I'm attempting to create a typings file for this simple queue implementation.

In my TS project, I've created a folder called customTypings and pointed to it in the typeRoots property of my tsconfig.json file.

Here's what my .d.ts file looks like:

declare module 'queue-fifo' {
    export default class Queue {
        constructor();
        isEmpty(): boolean;
        size(): number;
        clear(): void;
        enqueue(data: any): void;
        dequeue(): any;
        peek(): any;
    }
}

I import is as: import Queue from 'queue-fifo';

And then I try to create an instance of the Queue class: const queue = new Queue();

At this point, I get no type errors in VS Code, nor do I get any compilation errors. However, when I try to run my code through the debugger, I get:

Exception has occurred: TypeError
TypeError: queue_fifo_1.default is not a constructor
    at BinarySearchTree.bfs (/../binarySearchTree.ts:110:23)
    at testBst (/../binarySearchTree.ts:139:10)
    at Object.<anonymous> (/../binarySearchTree.ts:144:1)
    at Module._compile (module.js:632:14)
    at Object.Module._extensions..js (module.js:646:10)
    at Module.load (module.js:554:32)
    at tryModuleLoad (module.js:497:12)
    at Function.Module._load (module.js:489:3)
    at Function.Module.runMain (module.js:676:10)
    at startup (bootstrap_node.js:187:16)

If I break at that line, I see the Queue (what I imported) is undefined, but queue_fifo_1 is defined, and I can create an instance of the class using that name while in the debug console.

Can someone explain what I'm doing wrong in my declaration/consumption of the declaration that is causing this undesired behavior?

Joe Martella
  • 622
  • 1
  • 5
  • 18

1 Answers1

6

Solution

The queue-fifo module uses CommonJS-style export = Queue, which is not compatible with ES6 imports, and gives you no default export. The correct type definition will need to use the same export = style syntax:

declare module "queue-fifo" {
    class Queue {
        isEmpty(): boolean;
        size(): number;
        // etc
    }
    export = Queue;
}

Which can be imported using CommonJS-style import:

import Queue = require("queue-fifo");
const queue = new Queue();

ES6 import

If you're wondering if it's possible to import this module using ES6 syntax, you can probably use the namespace definition hack:

class Queue { /* ... */ }
namespace Queue { /* empty */ }
export = Queue;

Then import it using ES6 wildcard:

import * as Queue from "queue-fifo";
const queue = new Queue();

But this will only work in non-ES6 environments, for example bundlers like Webpack and Rollup make this work today, but future ES6 compliant module systems will not be able to make this work. See more here: What does "... resolves to a non-module entity and cannot be imported using this construct" mean?

Aaron Beall
  • 38,422
  • 20
  • 72
  • 94
  • Fantastic answer but one nit; "export as namespace" may be easily confused with the `export as namespace some_identifier` syntax used to define UMD modules in .d.ts files – Ryan Cavanaugh Nov 21 '17 at 05:44
  • Perfect. Thanks very much for the solution and the explanation. I was trying to quickly use this implementation in my project because when I was practicing my own implementation, I implemented it using `number` for simplicity. While waiting for an answer, I learned about TypeScript generics and converted my implementation to use that. Win-win! :) – Joe Martella Nov 21 '17 at 06:29