Typescript: Referencing external modules in application type declaration

Created on 3 Mar 2016  ·  15Comments  ·  Source: microsoft/TypeScript

How can I get a reference an external module in my application declaration file? Please look at following example:

// file meteor.d.ts
declare module "meteor/meteor" {
  export module Meteor {
  ...
  }
}
// file context.d.ts
// not part of any module
// not possible to do: import { Meteor } from "meteor/meteor";

declare interface IContext {
  Meteor: typeof Meteor; // how do I get the reference to Meteor?
}

Can this even be done?

Question

Most helpful comment

Workaround

What I am about to show you is hacky stuff that uses (abuses) the newly introduced declare global (global augmentation) introduced with TypeScript 1.8.

With the :warning: out of the way one can:

globals.d.ts

interface My {    // To introduce the name `My` to the global namespace
}

globalsAugment.d.ts

import meteor = require('meteor/meteor');
declare global {
    interface My {
        user: meteor.User;
    }
}

Caveat emptor :rose:

All 15 comments

Can this even be done?

Not at the moment. But I am sure there is an issue here by a TypeScript team member to allow this :rose: < I failed to find it right now though :-/

// not possible to do: import { Meteor } from "meteor/meteor";

Why?

It will remove the ambient context from the file and it will have be explicitly imported in every file. [EDIT] Much better answer by basarat ;)

Why?

If he does import meteor = require('meteor/meteor') then the file becomes a module. He doesn't want that. He wants the file to stay _global_.

you can not "fish" types out of a module and put them in the global space. your only way to get something out of a module, is to import it.

Why do you want to put these typings in the global name space? and why meteor.d.ts does not do that originally? what really happens at runtime? do these exist in the global namespace or not?

Found the issue I was looking for : https://github.com/Microsoft/TypeScript/issues/7125

Quoting a sample library from that issue:

declare var Promise: {
  // some stuff here
}
declare module 'bluebird' {
    export = Promise;
}

Basically if bluebird was written the way meteor is :

declare module 'bluebird' {
    declare var Promise: {
      // some stuff here
    }
    export = Promise;
}

Then it would not be possible to use typeof Promise in a global context if one does indeed do something like : window.Promise = require('bluebird') or something like that.

In the case of meteor is it relevant ... I don't know. I leave that discussion in the hands of @tomitrescak :rose:

But I am sure there is an issue here by a TypeScript team member to allow this

Instead of saying "to allow this" I should have said is "to enable this use case". Because the syntax is different. The issue (again https://github.com/Microsoft/TypeScript/issues/7125) allows the library definition author to enable the library usage in either _global_ or _module_ contexts :rose:

The reason here is to get reference to interfaces from that given module.
For example, module Meteor contains interface Meteor.User. If I want to have in my module a variable of type Meteor.User there is no way for my in the ambient declaration to get reference to it? Does it make sense?

module "meteor/meteor" {
  declare module Meteor { 
     export interface User { ... } 
  }
}

// my appp
declare interface My {
   user: Meteor.User; // impossible
} 

[EDIT] This example is almost the same as the one from @basarat

@tomitrescak Why not put My in a file called types.d.ts:

import meteor = require('meteor/meteor'); 
export interface My {
    user: Meteor.User;
}

And import the types wherever you need them:

import {My} from "./path/to/types"; 

I am similar file here : https://github.com/alm-tools/alm/blob/master/src/common/types.ts :rose:

That is the way I do it now. I was hoping that I will not have to import this interface explicitly as it literally exist in almost every file. I hoped for just declaring it globally and done ;) I had it working before until I encountered external modules.

Workaround

What I am about to show you is hacky stuff that uses (abuses) the newly introduced declare global (global augmentation) introduced with TypeScript 1.8.

With the :warning: out of the way one can:

globals.d.ts

interface My {    // To introduce the name `My` to the global namespace
}

globalsAugment.d.ts

import meteor = require('meteor/meteor');
declare global {
    interface My {
        user: meteor.User;
    }
}

Caveat emptor :rose:

@mhegazy I forgot to say massive THANKS!!!!! That thing is working very nicely and is doing exactly what I need. So once again ... THANKS!!!

@tomitrescak thanks for the kind words. Please let us know if you run into issues.

@basarat Hi, I would like to know if there is already a non-hacky way to achieve this use case.

@basarat Is this still the only way to go? What are the downsides of this hacky method?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Antony-Jones picture Antony-Jones  ·  3Comments

uber5001 picture uber5001  ·  3Comments

MartynasZilinskas picture MartynasZilinskas  ·  3Comments

fwanicka picture fwanicka  ·  3Comments

jbondc picture jbondc  ·  3Comments