2

I'm trying to create a declaration file for minimal-bit-array. Here's its essence:

function A() { this.data = 'foo'; }

A.prototype.serialize = function() { /* ... */ };

module.exports = A;
module.exports.deserialize = function(x) { /* ... */ };

How do I type this?

1 Answer 1

4

This is how:

declare module "a" {
  namespace A {
    export interface X { /* ... */ }
  }

  interface A {
    data: string;
    serialize(): A.X;
  }

  const A: {
    new (): A;
    deserialize(obj: A.X): A;
  };

  export = A;
}

This counter-intuitive namespace + interface + const sandwich gets successfully merged into an unambiguous something that TypeScript can understand. Consumption example:

import * as A from "a";

const a: A         = new A();
const d: A['data'] = a.data;
const x: A.X       = a.serialize();
const amazing      = A.deserialize(x);

Why the sandwich?

Theoretically, namespace A and const A are doing the same thing: describing module.exports of a. But the constructor - new (): A - can be declared only in an interface, while the TypeScript-only stuff - interface X - can be defined only in a namespace. That's why both are needed at the same time. There's no way to export interface X otherwise. At least as far as I know. Thankfully, TypeScript knows how to merge them.

In the meanwhile, interface A is just a convenience. When referencing A somewhere in the code in a context where a type would be expected, this is the interface that you get. The other two - namespace and const - aren't types, so there's no ambiguity. Could keep it in namespace A, however, if this is too much magic, and reference it as A.A.

Tested on 2.8.

Update

A slightly cleaner approach:

declare module "a" {
  namespace A {
    export interface X { /* ... */ }
  }

  class A {
    static deserialize(obj: A.X): A;
    data: string;
    constructor();
    serialize(): A.X;
  }

  export = A;
}

Classes happen to fit this scenario nicely. This is semantically equivalent to the original approach too, since classes are essentially syntactic sugar on top of functions. Check this playground to see what an implementation of the class above would get transpiled into.

Tested on 2.8.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.