Joe Martella
Joe Martella

Reputation: 752

How do I use a class defined in a TypeScript declaration file?

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?

Upvotes: 4

Views: 6253

Answers (1)

Aaron Beall
Aaron Beall

Reputation: 52133

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?

Upvotes: 7

Related Questions