Compiler Behavior Question

Topics: General
Feb 12, 2013 at 7:12 AM
Edited Feb 20, 2013 at 12:25 AM
We in the process of adopting TypeScript in our HTML5 high-end games platform (see http://turbulenz.com to try some of the games). In general it's all going very well, but there are a few little problems I've had with the behavior of the compiler.

We ship a JavaScript library made up of about 80 files, which I've turned into .ts files. Naturally, a lot of files use the types defined in other .ts files, and (this being JS) there are even some circular dependencies. So we've had to divide the files into conceptual 'modules' (which can depend on other modules), each of which produces a .js and .d.ts file.

If we use '/// <reference ...' to refer to the types in other .ts files then the resulting .js and .d.ts each contain all the code and declarations for dependent modules. When an app then uses multiple modules from our engine we get a lot of errors because types are declared multiple times. To work around this, we don't use the '/// <references', but use the dependencies between modules specified in our build scripts to build the .js and .d.ts for dependent modules, and then pass those to the compiler on the command line.

This works fine, but since there are no '/// <reference ...' statements in the code, the Visual Studio plugin is unable to resolve types in other files, even in the same project. On the one hand, the VS plugin forces us to use references to other .ts files (I can't find a way to set up a project that doesn't require this), but adding references pushes all dependent code and decls into the output from each module.

We are currently working around this by including references to keep VS happy, and using the --noresolve flag when building our library. We then have a second 'checking' build which builds each file individually (without --noresolve), thereby ensuring that the references are maintained for the people using VS.

Before I start submitting feature requests I'm interested to know if I've overlooked something and should be doing the build in a different way. Or maybe this is just something that hasn't been addressed in the compiler yet.

Thanks in advance for any suggestions.
Developer
Feb 12, 2013 at 6:03 PM
Could you post an extracted example of what your project structure looks like and why you get into issues of duplicated types? It's a bit hard to visualize from the write-up, but maybe an example can make it clear what the problem is.
Feb 19, 2013 at 9:20 AM
Edited Feb 19, 2013 at 9:21 AM
Thanks for your reply.

Here's a simplified example of the kind of 'module' setup we have:
src/
  utilities/
    utilities.ts
  scene/
    geometry.ts
    scene.ts
    scenenode.ts
  texturemanager/
    texturemanager.ts
where 'scene' and 'texturemanager' both use the classes defined in utilities.ts. Let's say we build each directory as a 'module', so we end up with utilities.js, scene.js and texturemanager.js (and the respective .d.ts files) and we ship these to developers. Developers might use either scene.js or texturemanager.js, or neither, or both.

If 'scene' and 'texturemanager' both use:

/// <reference path="utilities.ts" />

to get to the type information in 'utilities', then both scene.d.ts and texturemanager.d.ts contain the class declarations from 'utilities'.

Now, if a developer wants to use TypeScript and the .d.ts files we ship, and they use code from both 'scene' and 'texturemanager', (i.e. they reference or include on the commandline both scene.d.ts and texturemanager.d.ts) they will get 'Duplicate identifier' errors for all classes in 'utilities'

If 'scene' and 'texturemanager' reference utilities.d.ts then the declarations aren't replicated like this, but Visual Studio doesn't pick up any type info or changes until the build is run.

As mentioned, we can work around this by having several build modes, but I was a bit surprised that what I'm doing seems not to be compatible with the references system as it is now, so I'd be very interested to get your input.
Feb 19, 2013 at 1:51 PM
I'm sure Anders will come back on this, but
If we use '/// <reference ...' to refer to the types in other .ts files then the resulting .js and .d.ts each contain all the code and declarations for dependent modules.
and
to get to the type information in 'utilities', then both scene.d.ts and texturemanager.d.ts contain the class declarations from 'utilities'
doesn't seem right. As far as I'm aware reference includes shouldn't cause types to be imported from the referenced files.

What compiler options are you using?
Feb 19, 2013 at 4:18 PM
I guess you are compiling with the --out myoutfile.js option.

It turns out that when this option is on and you have <reference path="external.ts" /> then the contents of "external.ts" are included.

One way to fix this is to make sure you only include external definitions files, e.g. <reference path="external.d.ts" />.
Feb 20, 2013 at 1:05 AM
Thanks for your reply. Yes, we are using --out to control where the output for each module goes. It sounds like this is just a bug then, not the expected behaviour.

As described, you are right that if we reference the .d.ts files then the type information doesn't propagate into the output .d.ts files, but then Visual Studio is working with old (or non-existent) .d.ts files until we run a build. Anyway, if this is just unintended behaviour then that would explain the apparent conflict with what I'm trying to do.

If you don't mind me dragging this out a big more, the references are still not ideal. We only use them to keep Visual Studio happy, and they are difficult to maintain (we need a separate build config to check that there are none missing). Ignoring the problem of repeated type info, perhaps we could use the references to express all the dependencies (currently this info is held in our build files), but it would be nice to be able to get the compiler to flag invalid or outdated references, and maybe be able to pass search directories on the command line. TBH, I was quite surprised that TS doesn't automatically find types in other files of the same project.

Finally, I wanted to bring up the error handling behaviour of the compiler. I would expect people to use the compiler in one of two ways: 1) ignore type errors and just generate javascript regardless (e.g. when first starting to migrate an existing project), or 2) fail completely if type errors are found. The default behaviour seems to be half way in between. Type errors cause the compiler to exit with non-zero error code, but the .js files are always output (barring a full syntax error). So with most build systems the compiler is not re-run on the next build (because the output file already exists) and the errors are hidden.

Just for your reference, I've added --ignoretypeerrors and --failonerror flags (and made a few tweaks to the code gen) in this repo

Anyway, these are all subtle points. I'm very pleased with TypeScript and it's fully operational in our main development line. A lot of game developers are used to static languages, and (rightly) nervous about big projects in JavaScript, so it's great to be able to give them the TypeScript option. I'm sure you've seen hundreds of these but just in case you like reading them, I've written up how we migrated the engine and why TypeScript made it possible.
Feb 20, 2013 at 2:34 PM
@dtebbs,

I empathise with regard to the problems with the <reference> include mechanism.

There is a bug ticket out as well as a related discussion. It will help if you are able to contribute your thoughts.

Thanks
Noel