3
votes

Here is my use case:

We have a very lightweight abstraction around our Promise library because we've changed it several times now for various reasons and we prefer not to have to change the code throughout our application.

I want to add a middleware to the express.Request instance which I can easily do by augmenting the interface, like so:

declare namespace Express {
    export interface Request {
        loginP(user: any): void;
    }
}

Put this in a .d.ts file by itself and everything works great, which is awesome.

HOWEVER. Here is my trouble; in my abstraction, I re-export the interface of the promise library we are using so that we can use it throughout the application and have good type checking without requiring that everywhere know where it came from:

import * as bluebird from 'bluebird';

export {bluebird as PType};

This works really well; however, augmenting interfaces only works if the .d.ts file doesn't export or import anything, so when I try to have loginP return PType<void>, the type it actually returns, the augmentation suddenly stops working:

import {PType} from '../lib/Promise';

declare namespace Express {
    export interface Request {
        loginP(user: any): PType<void>;
    }
}

(With the above code, the member loginP is not found anywhere on the interface). If I remove the import {PType} line everything works fine -- except that I can't return the type I need.

Any ideas? I'm using typescript 1.8.9 with a tsconfig file, building with tsc -p .

1
What is this ../lib/Promise file? Is it a .ts or .d.ts or something else? What happens if you use /// <reference path="../lib/Promise" /> instead of the import?Nitzan Tomer
It's a .ts file, and adding that reference does nothing noticeabletaxilian

1 Answers

3
votes

I think your problem is that you are trying to perform module merging (as soon as you have export/import statement in your code - module become external). Even though its not directly supported - you can still do this.

Below I am posting code copied directly from typescript handbook as a sample of how to do this:

// observable.ts stays the same
// map.ts
import { Observable } from "./observable";
declare module "./observable" {
    interface Observable<T> {
        map<U>(f: (x: T) => U): Observable<U>;
    }
}
Observable.prototype.map = function (f) {
    // ... another exercise for the reader
}


// consumer.ts
import { Observable } from "./observable";
import "./map";

let o: Observable<number>;
o.map(x => x.toFixed());

Hope this helps.