Get UI project to recognize types in Library project

Topics: General
Mar 11, 2014 at 12:04 AM
I'm having a hard time getting a UI project I'm working on to recognize types in a library that I've written in TypeScript and I'd love some help.

The library project and the UI project are both generated from the TypeScript HTML Application template and are in the same solution. I have VS 2013 Pro and TypeScript 1.0 RC installed. The structure is something like this:

MyIdea\src\Class ... etc.

If this were C#, "MyIdea" would be the business DLL, and "MyIdea.WebUI" would be the app.

The code looks something like this:

export import Class1 = require("./src/Class1");
export import Class2 = require("./src/Class2");
/* etc... export / import for each class */
export var Version: string = "0.1.0-alpha.6";
MyIdea\src\Class1.ts (and all of the other classes):
import MyIdea = require("../MyIdea");
class Class1 {
   /* some stuff */
export = Class1;
The library works great when minified using the optimizer that is part of Require.js (using AMD) and everything is usable via the MyIdea module, so you can do MyIdea.Class1, MyIdea.Class2, etc.

So here's the confusion I'm having: I can't seem to get types working in my UI project.

I've tried the following:

Adding the --declaration switch to tsc so that I get a d.ts file for each TypeScript file, then adding a MyIdea.WebUI\Scripts\_references.ts with this as the first line (this is where the tsc output goes):
///<reference path="../../MyIdea/build/output/myidea.d.ts" />
Also, I tried to reference the original myidea.ts file directly, but it didn't seem to make the MyIdea object appear in Intellisense.

Since I'm using Durandal and I wanted require to work for both importing the TypeScript references and also for loading the required modules at runtime, I put this line in one of my UI pages:
import MyIdea = require('myidea');
And in the _references.ts file, I added something like this:
declare module 'myidea' {
    var MyIdeaStaticModule: any;
    export = MyIdeaStaticModule;
My require config looks like this:
    paths: {
        "myidea": "../lib/myidea.min"
        /* more ... */
This worked to have TypeScript be happy about using "MyIdea", and the resolution to the built and minified JS file worked at runtime (../lib/myidea.min.js), but of course MyIdea was of type "any" due to the fake module so it wasn't very useful. What is the correct way to get MyIdea to show up in my UI page as a valid reference to the root module in my other project and to get strong typing in the UI project?

If you want to see the actual code, it's here on this branch:

It's a TypeScript project of reasonable size and scope, and who knows it might be useful for you guys to do some profiling...

Thanks very much
Mar 11, 2014 at 4:47 PM
Did you try importing the required class directly in UIStuff.ts:

import Class1 = require('../../MyIdea/src/Class1');

The path is case sensitive. I'm not sure why you're carrying out the import/export in MyIdea.ts (I gues it's something to do with RequireJS finding the modules)
Mar 11, 2014 at 7:02 PM
Edited Mar 11, 2014 at 7:16 PM
I didn't try that specifically. I was hoping to be able to achieve this via the generated d.ts files instead of the .ts files, and ideally by just referencing the root MyIdea.d.ts file which I assumed would then "include" the references to all of the individual class d.ts files.

The reason for the import/export is to generate essentially a common root namespace for the library. I believe that this would allow code in MyIdea.Class1 to call static methods like MyIdea.Class2.DoStuff() or instantiate instances of MyIdea.Class3, but only use a single variable MyIdea in the global namespace. Unless I am mistaken, it is also the only way to get this behavior without requiring every single class to be in a single .ts file. Is this a mistaken premise? If there is a better way to get the ability to keep separate classes in separate TS files, but be able to reference them via a common root, that would be very helpful to know; I'm writing a library, not a series of scripts.

By the way - at runtime, the UI doesn't use the individually compiled separate js files that correspond one-for-one with the ts files - it uses a built/minified version of the library that r.js generates. I would like to avoid having to program each page to be aware of which classes are used if at all possible because it will cause difficulties with require.js - Ideally I'd like one require call that tells TypeScript which d.ts to import at design time and that will work with require.js at runtime to load the correct minified and bundled JavaScript file.

At the end of the day, all I want is for any of my ts files in the UI project to realize that there will be a MyIdea variable in the global JS namespace that is of type MyIdea. This works automatically within the same VS project, but my problem is getting it to work in the different project within the same solution. Any ideas are welcome but as I said, I would like to avoid having to reference each individual .ts project from the library project because I have d.ts files already compiled for each of them.

My ultimate goal is to allow other developers to download only a d.ts file (or worst case a collection of d.ts files) and the minified/bundled JS file for the library and let them create new experiences with it, so as you can see, directly referencing the .TS files in the other project kind of breaks this vision.

Thank you so much for the reply - your team has done amazing work with TypeScript and I would not have been able to create this library in the first place without your incredible efforts to date.
Mar 15, 2014 at 2:46 AM
I finally got my UI to recognize the d.ts file.

Of course, something was wrong so I "used Process Monitor" (tm).

I then observed that this line in my code:
import MyIdea = require('../../MyIdea/build/output/myidea.d.ts');
Was resulting in the TypeScript compiler to search for:
  • myidea.d.ts.ts
  • myidea.d.ts.d.ts
  • myidea.d.ts.str
  • myidea.d.ts.d.str
And then it searched for all four of those files in every folder all the way back to my drive root... sort of.

Since it was first searching for myidea.d.ts.ts, I changed my import statement to be this, and types started working correctly in my UI library.
import MyIdea = require('../../MyIdea/build/output/myidea.d');
Funny stuff!

@nabog - thanks again for your earlier answer. I think I may have had a case issue because I tried again later and the .ts file did light up Intellisense properly.
Mar 15, 2014 at 3:14 PM
Edited Mar 15, 2014 at 3:17 PM
So in addition to the fact that I am a terrible programmer, I believe that there are two issues with TypeScript which, if they had not been the case, I would not have gotten stuck on this for nearly a week. I've opened two issues:

"Requiring a d.ts file does the unexpected"

"Separate finding the reference from the emitted JS"

To summarize the workaround for TypeScript 1.0 RC for others who may come across this:

If you have a d.ts file (or set of d.ts files that are related) that defines an external module, you have to put the d.ts file in either the same folder as your .ts file that imports it, or a folder closer to the drive root. if you put a line like import MyLibrary = require("MyLibrary"), TypeScript 1.0 RC will case-sensitively search the current folder for both a MyLibrary.ts and MyLibrary.d.ts file (in that order), and then recursively search each folder back to the drive root (for .ts and .d.ts) to find it. So if you want to use this import in many different .ts files in your project, you have to just accept the fact that the d.ts file needs to live at the root of your project or in a folder even up from that (closer to the root). Then when you use require.js, you will have a clean library name to use in your config call.

Thanks to the TypeScript team for your amazing language. It really is great, and I hope that this feedback is accepted in the manner in which it is intended - to hopefully help improve an already amazing product. I would never have been able to create the project that I'm now writing a UI for had I had to do it in JS - I would have simply lost patience. Thanks again.

Lastly, if I am just being a terrible programmer here (as usual), and there is an easy workaround for the above, I would appreciate it if someone would let me know. Again - thanks so much.
Mar 15, 2014 at 11:46 PM
@nabog I've noticed --d used along with external modules do weird stuff and it only confuses people. Perhaps this particular combination should be disabled as its current state is not ideal?

e.g almost the exact same question :

A simple example to prove consider the file system:
|-- a.ts
|-- b.ts
import b_lib = require('./b');
export var b = b_lib;
export class A{};
export class B{};
Compile with --d --module amd it generates:
import b_lib = require('./b');
export declare var b: b_lib;
export declare class A {
export declare class B {
You can see that a.d.ts is already invalid