While I was doing a lot of refactoring and cleaning up on a Typescript project – to be able to go further with it, and to make it work good with the newest version of the compiler – I re-thought my setup. And found a way I’m pretty happy with so far. So I’d thought I’d share it.
Disclaimer: this is an ongoing experiment, nothing to be considered as a best practice. I highly appreciate any comments.
Highly recommended read “7 Months with TypeScript” by Ted Patrick : http://tedpatrick.com/2013/06/25/7-months-with-typescript/
The project is a fairly large Single Page Application one, and it was rather cluttered when I started my refactoring. Test, experiments, dead code and the actual application code in one and the same folder structure, and one and the same Visual Studio project. (I only deployed the fresh parts though).
Be sure that you are not hit by tooling issues
If you’re having problems with Visual Studio not compiling a particular typescript file, check if all the .ts and .d.ts files have BuildAction property set to TypeScriptCompile. Seems to be a bug somewhere in the TS tooling which makes that property set to Content at rare cases. This issue.
Another VS gotcha is the ts files are being saved as ANSI which dows not work with international characters, should have encoding UTF-8 just like any other VS code file. This issue.
A third thing I bumped into on an upgraded project was I had an interface with an “export” declaration in one of my typescript files. From 0.9.1(?) that makes the compiler treat the whole file as an external module, without any further notice inside Visual Studio. So the class I had in the same file was never picked up, and did not appear in the intellisense. This is by design, but I really think VS should give me a warning about it, a TSC compile does.
Clean up the project
With the newest version of Typescript, the tooling in Visual Studio automatically references all included typescript files (d.ts and .ts, if set to TypeScriptCompile as they should be automatically). This means you should remove unused pieces of code, and unused definition files from your project, otherwise you are slowing down the compiler.
Treat the Solution as any C# one, keep separate projects for different uses
I took a step back, cleaned up the project as much as I could and realized that one way to keep the compiler happy, and my project easier to deal with – was actually to treat my solution pretty much as any C# solution. That is I now have separated projects, one for the js “assembly” and one for the actual application, and one for tests.
So this is my current solution structure (all these are Visual Studio HTML Application with Typescript projects):
- MyApp.Core [only typescript classes]
- MyApp.Core.Tests [karma based tests]
- MyApp.HtmlApplication [startup module and resources]
- MyApp.Experiments [old stuff and experiments]
Most of my coding goes into MyApp.Core obviously. There are all my classes. And as I mentioned the compiler now automatically references all typescript files, and all defintions, in the project. So I have no “reference path=” code in my files.
My assembly file
So MyApp.Core.js is the file I copy to my other projects where I need it. To get type checking and intellisense I also let the compiler to create a definition file.
I cannot (as far as I know) specify any actual order of the compiled typescript files. But, just like in C#, that does not matter as nothing is being run before I explicitly say so. I have a MyApp.Startup class with all necessary startup code. And that’s the one I instantiate from my Html application, on document ready. (Or from my tests).
A problem with this setup is as you probably note is I do not have the ts files in the running application, nor in my testproject, and because of that I get no mapped ts debugging. Currently I’m fine with debugging the js-files.
Tweak the project settings in the .csproj file
To make the compiler create a single “assembly” file + a declaration file you will need to tweak the project file slightly. 1) set the TypeScriptOutFile option to a file name, and 2) set the TypeScriptGeneratesDeclarations to true.
You do this by unloading the project, do your edits (xml) and then reload the project file. Obviously you need to be careful as the settings inside the file affect your project. However – it’s just code, and Visual Studio will inform you if you mess up. Keep a copy to be safe.
This is how my typescript build setup looks like: (MyApp.Core.csproj):
<PropertyGroup Condition="'$(Configuration)' == 'Debug'"> <TypeScriptTarget>ES3</TypeScriptTarget> <TypeScriptIncludeComments>true</TypeScriptIncludeComments> <TypeScriptSourceMap>true</TypeScriptSourceMap> <TypeScriptGeneratesDeclarations>true</TypeScriptGeneratesDeclarations> <TypeScriptModuleKind>AMD</TypeScriptModuleKind> <TypeScriptOutFile>myapp.core.js</TypeScriptOutFile> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)' == 'Release'"> <TypeScriptTarget>ES3</TypeScriptTarget> <TypeScriptIncludeComments>false</TypeScriptIncludeComments> <TypeScriptSourceMap>false</TypeScriptSourceMap> <TypeScriptModuleKind>AMD</TypeScriptModuleKind> <TypeScriptOutFile>myapp.core.js</TypeScriptOutFile> </PropertyGroup>
Additional build instructions to copy files automatically to the test project
I also added instructions to make it copy the created files to my tests and application projects:
xcopy /y $(ProjectDir)myapp.core.* $(ProjectDir)..\MyApp.Core.Tests\<br /> xcopy /y /s $(ProjectDir)\Application\Interfaces\.* $(ProjectDir)..\MyApp.Core.Tests\Interfaces\
The test project consists of the compiled myapp.core.js, the myapp.core.d.ts, the interfaces from the core project and the actual test files.
Still happy with Typescript
Sidenote, somewhat related: Classes vs Modules
I have changed my mind slightly about that. It is simply another style to code class free, and mixing the two can create confusion. So to keep my code clean and easy to deal with I decided to use classes only in my core project.
For the html application small startup code, and in tests, modules often makes sense tho.