2

I am trying to work with TypeScript and Express. I've loaded in the type declarations from Typings and they look like this:

// Generated by typings
// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/express/express.d.ts
declare module "express" {
    import * as serveStatic from "serve-static";
    import * as core from "express-serve-static-core";

    /**
     * Creates an Express application. The express() function is a top-level function exported by the express module.
     */
    function e(): core.Express;

    namespace e {

        /**
         * This is the only built-in middleware function in Express. It serves static files and is based on serve-static.
         */
        var static: typeof serveStatic;

        export function Router(options?: any): core.Router;

        interface Application extends core.Application { }
        interface CookieOptions extends core.CookieOptions { }
        interface Errback extends core.Errback { }
        interface ErrorRequestHandler extends core.ErrorRequestHandler { }
        interface Express extends core.Express { }
        interface Handler extends core.Handler { }
        interface IRoute extends core.IRoute { }
        interface IRouter<T> extends core.IRouter<T> { }
        interface IRouterMatcher<T> extends core.IRouterMatcher<T> { }
        interface MediaType extends core.MediaType { }
        interface NextFunction extends core.NextFunction { }
        interface Request extends core.Request { }
        interface RequestHandler extends core.RequestHandler { }
        interface RequestParamHandler extends core.RequestParamHandler { }
        export interface Response extends core.Response { }
        interface Router extends core.Router { }
        interface Send extends core.Send { }
    }

    export = e;
}

Now I'm trying to extend the prototype for all Response objects like so:

const express = require('express');
express.response.sendWrapped = function(obj: Object, meta?: Object) {
    return this.json({
        data: obj
    });
};

Now I just need to get the extension into the typings. I would love to just extend this method into the existing definition, much like how I have extended it in the prototype, but I am unsure how to do so.

Since this is a personal extension of the type definition for the library, I do not believe this belongs back with the master typing set and I should not have to modify them manually for my own purposes. What is the best way to extend them in my own way? Can I do it without overriding every other piece dependent on Response?

2 Answers 2

2

Using TypeScript 2.0 or a pre-release of it (typescript@next) you can use module augmentation syntax to augment the definition of express to include your new response definition:

import express = require('express');

declare module "express" {
    namespace response {
        function sendWrapped(obj: any, meta?: any): void;
    }
}

express.response.sendWrapped = function(obj: Object, meta?: Object) {
    return this.json({
        data: obj
    });
};

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

3 Comments

Does this have to be declared in the TS file or can it go in the d.ts?
you can do this in both a .ts and .d.ts
How did you get your typescript 2.0 to recognize the express framework?
-1

A few things:

(1) There's no response in the declarations you included, there's a Response, so it should be: express.Response.sendWrapped.

(2) You did not extend the prototype for all Responses, you added the sendWrapped function to the class itself (I assume that Response is a class).
If you want to add to the prototype it should look like:

express.Response.prototype.sendWrapped = function(obj: Object, meta?: Object) {
    return this.json({
        data: obj
    });
};

(3) You can add to an existing type like so:

namespace express {
    interface Response {
        sendWrapped(obj: Object, meta?: Object): any;
    }
}

More about that in Declaration Merging


Edit

If it's in a d.ts file then you need it to be:

declare namespace express {
    ...
}

I'm not sure why it doesn't let you merge declarations and I don't have this setup to test it on my own, but I have another solution for you which is, in my opinion, better.

Just extend express.Response add what ever you need in there, for example:

class MyResponse extends express.Response {
    constructor() {
        super();
    }

    sendWrapped(obj: Object, meta?: Object): express.Response {
        return this.json({
            data: obj
        });
    }
}

This is better because you're not as vulnerable for changes in the express api, and more than that, this is what object oriented is for.

7 Comments

There is a bit of a mismatch here because of how Express works under the hood. The actual prototype is under response, while the definition is under Response. But that's an implementation detail. What I need is to just update the interface to work accordingly. I'll try your suggestion. Thanks!
Unfortunately, none of these seem to work for me. I was most hopeful about #3, but when it's own .d.ts file I get this error: A 'declare' modifier is required for a top level declaration in a .d.ts file.. If I try to add the namespace in a normal .ts file, I get this error: Import declaration conflicts with local declaration of 'express'
The declare namespace suggestion eliminates the compile errors, but it doesn't seem to have any effect on Response where it is called. Unfortunately, extending the class is not a very clean approach here. Response objects are passed to other functions via type inference, and I would have to override every single one of them to add a new method. For example: router.get('/users/', (req, res) => /* res is of type Response via declarations */ );
I believe there is an outstanding TypeScript issue for supporting this better. github.com/Microsoft/TypeScript/issues/819
Looks exactly what I said, you declare it again and add the needed members/methods (only I used interface and they used class). If there's no compilation error but it doesn't have the method in run time then the way you actually add the method is wrong, probably something to do with the prototype issue you mentioned.
|

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.