"reference" vs "import" for referencing only type

Topics: General
Feb 17, 2014 at 9:05 AM
Edited Feb 17, 2014 at 9:06 AM
What's the best method of importing a class into another purely for the purpose of referencing it's type?

e.g.
class Hello {

    constructor(private world: World) {
    }

}
Should the 'World' type be imported via a reference or an import statement?
/// <reference path="World.ts" />
The reference method doesn't seem to work consistently. Sometimes it will allow us to reference the type World, others not (we haven't managed to pin down the reason yet).
import World = require('World');
Import works consistently but causes code to be rendered out to the JavaScript that is completely unnecessary:
// Require statement that's not needed in rendered code
var World = require('World');

var Hello = (function () {
    function Hello(world) {
        this.world = world;
    }
   
    return Hello;
})();

module.exports = Hello;
Feb 17, 2014 at 2:36 PM
Dan_s wrote:
What's the best method of importing a class into another purely for the purpose of referencing it's type?
The bit "purely for the purpose of referencing its type" kind of answers the question itself. When you reference a type with <reference>, you're basically saying "Trust me, there is such and such a type, and it will be available at runtime".

Import on the other hand depends on the existence of a physical file at the import location.

There used to be a number of bugs on the <reference> tag, but at the moment I believe they've all been resolved.
Feb 17, 2014 at 3:38 PM
Edited Feb 17, 2014 at 3:40 PM
So just to clarify, the following should work?

World.ts
class World {

    constructor(private name: string) {

    }

}

export = World;
Hello.ts
/// <reference path="World.ts" />

class Hello {

    constructor(world: World) {

    }

}
At the moment this causes the following error when compiled with "tsc Hello.ts --module commonjs":
error TS2095: Could not find symbol 'World'.
There's also no way to alias the imported type when using the <reference> tag (unlike when import is used), which could easily cause issues.
Feb 17, 2014 at 4:59 PM
@Dan_s, depends on what you want to do.

I inferred from your comment "Require statement that's not needed in rendered code" that you're probably writing something for the browser, and you're loading the script via a <script> tag.

For that case, you'd need to namespace your code using modules like the following:

module foo {
    export class World {
        constructor(private name: string) {
    }
  }
}

And in the calling code
/// <reference path="World.ts" />

class Hello {
    constructor(world: foo.World) {
    }
}
There are a few different ways to structure modules. See also https://typescript.codeplex.com/discussions/532302
Developer
Feb 17, 2014 at 6:51 PM
/// reference path vs import/require is not really about types vs values. With either one you can use only types from the referenced file(s). In the case of import/require the compiler will not emit the module loading code in the JavaScript if it determines the module was imported only for purposes of type information (which doesn't need to be available at runtime). You should choose between internal modules (/// reference path) or external modules (import/require) for other reasons than this. You appear to be confused about the difference based on your second example (mixing export= and /// reference path), I would recommend starting here to better understand the difference: https://typescript.codeplex.com/wikipage?title=Modules%20in%20TypeScript&version=12
Feb 17, 2014 at 9:25 PM
We currently use 'import' combined with 'export ='. The query about reference is because the module loading code is being generated when the import is being used purely for type information. Perhaps a bug?

Real world example:
import Screenshot = require('../Image/Screenshot');

class Capture {

  private _resultCounter: number;

  constructor(private _screenshot: Screenshot,
              private _testName: string) {


    this._resultCounter = 1;
  }

  public resultImage(done: () => void) {
    this._screenshot.take(this._testName + '/' + this._resultCounter + '_actual.png', done);

    this._resultCounter++;
  }

}

export = Capture;
Is being rendered to:
var Screenshot = require('../Image/Screenshot');

var Capture = (function () {
    function Capture(_screenshot, _testName) {
        this._screenshot = _screenshot;
        this._testName = _testName;
        this._resultCounter = 1;
    }
    Capture.prototype.resultImage = function (done) {
        this._screenshot.take(this._testName + '/' + this._resultCounter + '_actual.png', done);

        this._resultCounter++;
    };
    return Capture;
})();

module.exports = Capture;
Note that the Screenshot object is being injected and the import is only for type information, but the require statement is still in the JavaScript.
Developer
Feb 17, 2014 at 9:43 PM
Yeah could be a bug that still exists, or it was possibly fixed already, I can't be sure yet. Can you share the contents of the Screenshot module? With the latest bits in the develop branch I see the correct codegen for the Capture module (ie no require codegen'd) with a trivial version of a Screenshot module like:
interface Screenshot {
    x: number;
    y: number;
    take(name: string, done: any): any;
}

export = Screenshot
so either the bug has been fixed already in the latest bits (I'm assuming you're using 0.9.5) or there's something specific to the way your Screenshot module is authored that's confusing the compiler and making me unable to repro it with a simple case.
Feb 17, 2014 at 9:59 PM
The Screenshot code is:
import fs = require('fs');
import webdriver = require('selenium-webdriver');

class Screenshot {

  constructor(private _fs: typeof fs,
              private _driver: webdriver.Driver) {

  }

  public take(saveAs: string,
              callback: () => void) {

    this._driver
      .takeScreenshot()
      .then((result) => {
        var buffer = new Buffer(result, 'base64');

        this._fs.writeFileSync(saveAs, buffer);

        callback();
      });
  }

}

export = Screenshot;
Developer
Feb 17, 2014 at 10:28 PM
Thanks. It looks like this has been fixed in our latest bits. I was able to repro what you're seeing with 0.9.5 but the latest bits in the develop branch properly omit the require from the codegen.
Feb 17, 2014 at 10:53 PM
Great, thanks for the feedback. I was seeing the module require statement rendered out in all the classes I'd checked so had assumed that it was the expected behavior; hence the confusion on if we should be using <reference> tag.