Typescript - Extending existing module declarations

I am still learning typescript, so I got stuck. Also couldnt find anything online.

I am using meteor js which has the following module declaration meteor.d.ts:

declare module 'meteor/meteor' {
    type global_Error = Error;
    module Meteor {
        /** User **/
        interface UserEmail {
            address: string;
            verified: boolean;
        }
        interface User {
            _id: string;
            username?: string | undefined;
            emails?: UserEmail[] | undefined;
            createdAt?: Date | undefined;
            profile?: any;
            services?: any;
        }

        function user(options?: { fields?: Mongo.FieldSpecifier | undefined }): User | null;

        function userId(): string | null;
        var users: Mongo.Collection<User>;
        /** User **/
    }
}

Now I have some additional Fields for a user, so I would like to extend the interface User in the module Meteor with some fields:

interface UserAdditonal {
    field1: string
    field2: string
    field3: string
}

How would I do that? I tried this:

declare module "meteor/meteor" {
    module Meteor {
        function user(options?: {
            fields?: Mongo.FieldSpecifier | undefined
        }): (User & userAdditonal) | null

        var users: Mongo.Collection<User & userAdditonal>
    }
}

Which throws an error meteor.d.ts(56, 13): 'users' was also declared here. and also only works in the same file. Also it is more like overwriting, and less like extending

Is it possible to do globally, so I declare it in one file and all other files that import 'meteor/meteor' get the extended types without importing my file?

Any help is greatly appreciated!


Solution 1:

Yes it is common to enrich your Meteor.users collection (although it is advised not to put too much data in it), and you can definitely tell TypeScript which extra keys you have added.

The idea is simply to enrich the Meteor.User interface. Because that interface is then used as the return type for Meteor.user() function and the document type for Meteor.users collection (as shown in the extract of the definition in your question), it will automatically be available whenever you use them.

// In a file that is imported somewhere

declare module "meteor/meteor" {
  module Meteor {
    interface User {
      field1: string
      field2: string
      field3: string
    }

    // If you already have an existing interface that you want to "merge"
    // into Meteor.User:
    interface User extends UserAdditonal {}
  }
}

Then do not forget to import the "meteor/meteor" module, and your extra fields should automatically be available:

import { Meteor } from "meteor/meteor";

Meteor.user().field1; // string

Meteor.users.findOne().field2; // string