15

Given a third-party TypeScript module defined like this:

// in /node_modules/third-party-module/index.d.ts
declare module 'ThirdPartyModule' {
    export interface ThirdPartyInterface {
        data: any;
    }
}

How can I augment this module to more strictly type the data property?

I've tried this:

// in /app/typings.d.ts
declare module 'ThirdPartyModule' {

    interface IMyCustomType {}

    interface ThirdPartyInterface {

        // causes a compiler error: Subsequent property declarations 
        // must have the same type.  Property 'data' must be of type 
        // 'any', but here has type 'IMyCustomType'.
        data: IMyCustomType;
    }
}

But this gives me a compiler error: "Subsequent property declarations must have the same type. Property 'data' must be of type 'any', but here has type 'IMyCustomType'."

If the third-party module instead defined the property as an actual type, like this:

// in /node_modules/third-party-module/index.d.ts
declare module 'ThirdPartyModule' {
    interface IAnotherThirdPartyInterface {
        something: string;
    }

    interface ThirdPartyInterface {
        data: IAnotherThirdPartyInterface;
    }
}

I could simply make my IMyCustomType interface extend this third party type:

// in /app/typings.d.ts
declare module 'ThirdPartyModule' {
    interface IMyCustomType extends IAnotherThirdPartyInterface {}

    interface ThirdPartyInterface {
        data: IMyCustomType;
    }
}

However, since the type is defined any, I can't extend it:

// causes a compiler error:  'any' only refers to a type, 
// but is being used as a value here.
interface IMyCustomType extends any {}
4
  • 1
    What is your use case ? Do you want to have stricter types for calls in your library ? Or do you need this interface for your own functions which require similar data structures ? Commented Feb 8, 2018 at 17:25
  • 1
    I'm attempting to override the type of Hapi's request.app property. The property is a generic place to store application data, so it is typed as any by the library's authors. However, since I know how this property will be used, I'd like to add typing information to this property to avoid casts. Commented Feb 8, 2018 at 17:32
  • 2
    Hmm, I must admit that I've never found a better answer than "use a locally modified version of the library" for this (and possibly trying to get such a version pushed upstream if it's better for everyone). Declaration merging isn't granular enough for some purposes and this is one of the pain points. I'd be interested in seeing a better answer than that too. Commented Feb 8, 2018 at 17:35
  • 1
    @jcalz you might not be able to redefine it, but you could have a typed alternative to it. Which in this case might be just as useful. Commented Feb 8, 2018 at 18:30

1 Answer 1

11

While you can't redefine the property, an alternative would be to define a new property, and augment the original object with this new property. While this solution is not generally applicable, if the property is on a class it can be done. In your case you mention in the comments hapi. Since the property is on the Server class we can define a new typed version of the property.

hapi.augment.ts

import *  as Hapi from 'hapi'

declare module 'hapi' {
    export function server(cfg: any): Hapi.Server;
    interface Server {
        typedApp:  {
            myData: string
        }
    }
}

Object.defineProperty(Hapi.Server.prototype, 'typedApp', {
    enumerable: false,
    get(this: Hapi.Server){
        return this.app;
    },
    set(this: Hapi.Server, value: any){
        this.app = value;
    }
});

usage.ts

import *  as Hapi from 'hapi'
import './hapi.augment'

const server = new Hapi.Server()
server.connection({ port: 3000, host: 'localhost' });
server.start();
server.typedApp.myData = "Here";

server.route({
    method: 'GET',
    path: '/',
    handler: function (request, reply) {
        reply(request.server.typedApp.myData);
    }
});
Sign up to request clarification or add additional context in comments.

1 Comment

Very interesting solution! This gets me closer to what I want. Thanks for the example!

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.