CHAPTER 3

image

Working with TypeScript

So now you’re up to speed with the new syntax and language features of TypeScript, it’s time to put them to use and include TypeScript in your own projects. In this chapter, you’ll look at how to get the most from the compiler and then how to include TypeScript in your Visual Studio projects.

The Compiler

Thus far, your interaction with the compiler has been to watch Visual Studio use it in the background when you’ve asked it to build your sample projects. In this section, you’ll look at how to use it manually so you can tweak its operation if necessary. The Web Essentials Options dialog box for TypeScript support that you saw in Chapter 1 (and that’s shown in Figure 3-1) has already given you a clue to the various options you can use, but there are some other points to consider.

9781430257257_Fig03-01.jpg

Figure 3-1 .  The Web Essentials Options dialog box for TypeScript

As you saw in Chapter 1, the TypeScript compiler is installed in the %ProgramFiles (x86)%Windows SDKTypescript<VERSION> directory on 64-bit systems (%ProgramFiles%Windows SDKTypescript<VERSION> on 32-bit) and is a single JavaScript file, tsc.js, that can be run using any ECMAScript 3–compliant JavaScript engine. Another file—tsc.exe—allows us to work with ithe compilerfrom the command line, and deals with the switches and file batches we want to compile.

image Note  The compiler’s current file I/O infrastructure supports only Node.js and Windows Script Host file APIs, which is not great news if you’re running on OS X or Linux/Unix. Get in touch with the team at the CodePlex site (http://typescript.codeplex.com/discussions/404060)  to request better native support.

Assuming the installation directory has been added to your PATH, you can call the compiler from the command-line by using the following syntax:

> tsc @<file>
> tsc [options] <fileList>
 

There are ten options you can send to the compiler. The first two tell the compiler to generate something in addition to compiled JavaScript:

  • --declaration requires the compiler to generate the .d.ts declaration file for the input files as well as the compiled JavaScript.
  • --sourcemap requires the compiler to generate the .map source map files needed for step-through debugging of your TypeScript files.

For example, calling

tsc --declaration test.js
tsc --sourcemap test.js
 

will produce the files test.d.ts and test.map, respectively, as well as test.js.

  • If you’re using TypeScript as a Node.js package, you can call tsc -w <fileList> to have the compiler watch all the files in <fileList> and recompile them when they have been altered.

The next few options tweak the pre- and post-compile tasks for the compiler:

  • --nolib requires the compiler to ignore its default and not to include lib.d.ts in its compilation even if you <reference> it explicitly in the file.
  • --out <outputFile> requires the compiler to output all its compiled JavaScript into a single file called <outputFile> rather than the default of producing one JavaScript file for each TypeScript file in the <fileList> (with the same name: a.ts becomes a.js, b.ts becomes b.js, and so on).
  • -e or --exec requires the compiler to execute the newly compiled script after it has been created.

Remember that scripts are run from top to bottom, so any code not in a class or module is run. There’s no single entry point, like a Main function in a .NET command-line application. Also, if you use this option, be sure your global functions aren’t expecting to run in a browser, or you’ll get errors straight away.  

tsc -e saveFaramir.ts
tsc --nolib shire.ts
tsc --out fellowship.js hobbit.ts human.ts elf.ts dwarf.ts
 

Another three influence the contents of the actual JavaScript emitted by the compiler:

  • -c or --comments requires the compiler to include the comments you added to the TypeScript file in the compiled JavaScript.
  • --module <type> requires the compiler to generate the specified <type> of modules during compilation. By default, it will generate commonjs modules (for Node.js scripts), but you can also specify amd modules (for example, for the RequireJS runtime module loader). See the section on modules in Chapter 2 for more information on the difference between the two.
  • --target <version> requires the compiler to emit JavaScript compliant with the specified <version> of ECMAScript. By default, it will generate script compliant with ECMAScript 3 (ES3), but you can also specify ES5 to have it emit ECMAScript 5–compliant script instead.
    tsc --comments howDidThisWorkAgain.ts
    tsc --module amd somethingForRequirejs.ts
    tsc --target ES5 newAndShiny.ts
  • Finally, you can also use the -h or --help option to bring up a quick recap of these options onscreen.

If you are setting a lot of options and including a large number of files in the compilation, you can copy all the switches and the file list into a text file and call tsc @file.txt to save typing it all out over and over again. File.txt, meanwhile, would have each switch and file to compile separated by a new line, like this:

--out fellowship.js
--module amd
--comments
hobbit.ts
human.ts
elf.ts
dwarf.ts

The File List

The file list you supply to the compiler is a space-separated list of all the files you want it to compile. This is much like the list you would send to the C# compiler (csc.exe), but with one difference. With csc.exe, you also need to include a list of references to the DLLs containing namespaces and classes your own C# code uses. This is not necessary with TypeScript. The tsc compiler will scan the top of each file in the list supplied for <reference file="..."> statements and include those referenced (both .d.ts and .ts) in its list for compilation. It does the same for files referenced via an import statement in your code.

It will also check those files for <reference> and import statements, include those files, and so on, until it has completely walked through the entire reference tree and has a complete list of files to compile. The net result is that you don’t need to include any declaration files in the file list for the compiler and perhaps fewer .ts files than you thought as well.

Also, unless you send it the --nolib option, the compiler will automatically include the lib.d.ts declaration file for the standard JavaScript runtime library and DOM API. The TLS does the same walk of <references> and import statements as it provides IntelliSense, so if IntelliSense appears to be working correctly across all your TypeScript files, you know your reference tree is intact as well, ready for compilation.

A QUICK COMPILER Q & A

Q: Can you include .js files in your compilation?A:  No. The compiler deals with only .ts or .d.ts files. Don’t try to pass it any “raw” JavaScript files, because it will just reject them. If you need to reference something in a .js file, you can do one of the following:

  • Create a declaration file for it (see the upcoming  “Declaration Files” section in this chapter).
  • Include an ambient declaration in your TypeScript file for the variable or function you need to use.
  • Rename a copy of your .js file to .ts and see if it compiles.

Q: Why doesn’t the compiler generate a file if I pass it a declaration file?A: If your file contains only comments, interface definitions, or other type information, the compiler does not generate any code, and so it does not generate a file, empty or otherwise (http://typescript.codeplex.com/workitem/284).

Q: Why does the compiler slow down when working with Windows 8 apps?A: It’s not specifically Windows 8 apps. The compiler slows down when working with really large files such as winrt.d.ts, which you need for Win 8 development (http://typescript.codeplex.com/workitem/265).

Q: How do I pipe compiler errors from tsc to my own log file rather than to the command line? (http://stackoverflow.com/questions/12897695/typescript-tsc-redirection-of-messages)

A: The compiler writes errors to stderr rather than stdout, so you’ll need to use the following command to pipe the errors:> tsc file.js > err.log 2>&1

Declaration Files

As soon as you start using Backbone.js, jQuery, jQuery UI, or any other third-party JavaScript library with your own TypeScript code, you’ll need to start using declaration files as well. Add a quick annotation at the top of your file, for example

/// <reference path="libjquery-1.8.d.ts" />

and the TLS can incorporate the interfaces and variables for jQuery into its IntelliSense prompts and type inference/checking. It’s a very neat system that demonstrates the need for ambient declarations (in case you’re wondering). Trouble is, what happens when the declaration file doesn’t exist for the library you want to use, be it a third-party library or one your own team has created? You have four choices at this point:

  • Double-check that it’s not available somewhere online.
  • Use an ambient declaration in your code to identify the library’s existence.
  • Use the TypeScript compiler to generate a baseline declaration file.
  • Roll your own declaration file.

Let’s look at each of these in turn.

Has Someone Else Written It?

As you know by now, the TypeScript team has written four declaration files:

  • lib.d.ts covers the standard JavaScript Runtime Library, HTML5 APIs, and the DOM API.
  • jquery.d.ts covers the definitions for jQuery 1.7.
  • winjs.d.ts covers the Windows Library for JavaScript (for Windows 8 JavaScript apps).
  • winrt.d.ts covers the Windows Runtime Library (for Windows 8 apps).

Beyond these four, the TypeScript community has already done quite a bit of work on both improving these four files and creating many more besides. Currently, the unofficial repository for declaration files is https://github.com/borisyankov/DefinitelyTyped. Here you’ll find updates for the four Microsoft declaration files and several dozen more libraries from AngularJS to QUnit to WebGL. And, of course, if you can’t find the declaration file on DefinitelyTyped, then Google may still be your friend.

image Note  There is a discussion at http://typescript.codeplex.com/discussions/398580 about an official repository for declaration files and how best to make them available (NuGet, download, something else) and another at http://typescript.codeplex.com/workitem/267 about how best to correlate the version of the declaration file with the version of the library against which it is written. Both are ongoing as this book went to press.

Substitute with an Ambient Declaration

All declaration files contain interface definitions for the classes defined in a library and ambient declarations for global variables of those classes. Without these, both the compiler and TLS will complain as soon as you use one of those global variables in your code, and it will not compile.

If you are sufficiently secure in your knowledge of the library you want to use, you can add an ambient declaration for the global variable you want to use and assign it the any type. For instance, let’s suppose you want to use jQuery but don’t have the declaration file at hand. You can declare its global variable like so:

declare var $ : any;
 

and continue to use jQuery with no issue. You’ll get no IntelliSense or type checking—everything will be of the any type—but that’s how we worked with jQuery before anyway.

Use the Compiler to Generate a Declaration File

You saw earlier that the TypeScript compiler will generate a declaration file for you if you pass it the --declaration flag. For example:

> tsc --declaration SimpleLibrary.ts
 

Assuming SimpleLibrary.ts compiles correctly, the compiler will generate SimpleLibrary.js for inclusion in your project releases and SimpleLibrary.d.ts for use in your development work.

This sounds like an ideal solution, and it is—for libraries written in TypeScript. Unfortunately for JavaScript libraries, the compiler accepts only valid TypeScript files. If I wanted to generate my own declaration file for jQuery, I couldn’t just download jquery.js, rename it to jquery.ts, and point the compiler at it: the compiler would throw many errors at it. And even if it did compile, the resultant declaration file would not contain much useful type information and perhaps lose some of the subtlety in the library. Because it is written in plain JavaScript, the compiler perceives all parameters to be of the any type rather than strings, numbers, and so forth. Remember: it is the TLS in the Visual Studio extension that infers types rather than the compiler.

All in all, unless you’re referencing a library written in TypeScript, this is the most unsatisfactory solution of the four. Even Microsoft went down the route of writing a custom script to produce its declaration files based on Web IDL and standards definitions rather than pass the actual JavaScript libraries through the compiler.

image Note  Interestingly, some developers have decided that the best solution is to rewrite their favorite libraries in TypeScript so declaration files can be quickly generated to match the latest versions—for example, backbone.ts at https://github.com/jbaldwin/backbone.ts.

Roll Your Own

Like it or not, your best shot may be to write your own declaration file. Remember that a declaration file is just a collection of interfaces and ambient declarations for global variables, values, and functions. For example, lib.d.ts starts by declaring a handful of familiar number-related values before going on to define the interface for the number object type and then a class variable for it:

declare var NaN: number;
declare var Infinity: number;
declare function eval(x: string): any;
declare function parseInt(s: string, radix?: number): number;
declare function parseFloat(string: string): number;
declare function isNaN(number: number): bool;
declare function isFinite(number: number): bool;
 
...
 
interface Number {
    toString(radix?: number): string;
    toFixed(fractionDigits?: number): string;
    toExponential(fractionDigits?: number): string;
    toPrecision(precision: number): string;
}
declare var Number: {
    new (value?: any): Number;
    (value?: any): number;
    prototype: Number;
    MAX_VALUE: number;
    MIN_VALUE: number;
    NaN: number;
    NEGATIVE_INFINITY: number;
    POSITIVE_INFINITY: number;
}
 

One note, though: remember to include a prototype property inside your variable declaration to generate the correct OO properties for JavaScript.

TypeScript in Your Projects

It’s time at last to look at how best to incorporate TypeScript into one of your own projects rather than experiment with it in one of its own. Necessity dictates being brief in this discussion about writing actual code in favor of how to set up your projects, so it goes without saying that you should also look at the eight sample apps available at http://typescriptlang.org/samples. Each demonstrates how TypeScript fits in with a certain kind of project and set of libraries:

  • Hello World: Basic TypeScript
  • Raytracer: DOM canvas
  • TodoMVC: Backbone.js and jQuery
  • ImageBoard: Node.js, Express, and MongoDB
  • JQuery Parallax Starfield: jQuery (natch)
  • D3 Visualization: D3.js (Data-Driven Documents) and HTML5 canvas
  • Warship Combat: jQuery and jQuery UI
  • Encyclopedia App: Windows 8 Store application

Be it a Web or Windows 8 Application project, where you previously had JavaScript, you can now have TypeScript generating JavaScript instead. It’s just a matter of adding a new TypeScript file to the project instead of a JavaScript file, isn’t it?

Mostly, yes. However, when using TypeScript with anything other than the accompanying HTML Application with TypeScript project template, you’ll need to tweak your project’s MSBuild (.proj) file to invoke the TypeScript compiler. You’ll look at that next and follow up with a few additional considerations to make if you’re adding TypeScript to either a Web Site project or a project that uses the ASP.NET 4.5 Web Optimization library.

If you’re an MVC user, you may also want to have a look at https://github.com/devcurry/typescript-template-for-aspnet-mvc, which as the URL suggests is a GitHub project developing a Visual Studio project template for using TypeScript with MVC 4.

image Tip  As this book went to press, Chris Sells had just posted online some new sample project templates for Windows 8 applications built with TypeScript. Find them at https://www.sellsbrothers.com/Posts/Details/12724

In Your MSBuild File

The key to your projects correctly integrating TypeScript into their compilation is in adding a call to the TypeScript compiler before the actual build. You know from Chapter 1 that if it is installed, the Web Essentials extension compiles our TypeScript files for us on the fly, but when we build the project, Visual Studio will compile them again based on a different set of criteria from those expressed in the Web Essentials Options dialog box (shown earlier in Figure 3-1). The build criteria can be found in the MSBuild file for your project—typically the .jsproj,  .csproj, or .vbproj file in the root directory of your project.

Let’s have a look at the MSBuild file for a TypeScript project and see what it does:

  1. Create a new HTML Application with TypeScript project in VS called CompilerTest.
  2. Add a second TypeScript file to the project. Call it second.ts.
  3. In the Solution Explorer window, right-click the project name and then select Unload Project.
  4. Right-click the project name again and click Edit CompilerTest.csproj file.
  5. To reload the project back into Visual Studio after you’ve finished looking at this, right-click the project name in the Solution Explorer window and then select Reload Project.

The relevant parts of the file are as follows.

The TypeScript Files and Their Dependencies

The project’s MSBuild file lists each TypeScript file within a <TypeScriptCompile> element. Each of these is a child of an <ItemGroup> element, although there may not be a 1:1 correspondence between the two. Each JavaScript file is listed within a <Content> element, indicating that the JavaScript file must be included when the project is published. Each <Content> element also has a child <DependentUpon> element documenting the relationship between the two.

<ItemGroup>
  <Content Include="app.js">
    <DependentUpon>app.ts</DependentUpon>
  </Content>
  <Content Include="second.js">
    <DependentUpon>second.ts</DependentUpon>
  </Content>
  <TypeScriptCompile Include="second.ts" />
  <TypeScriptCompile Include="app.ts" />
</ItemGroup>
 

Note that the second <TypeScriptCompile> element may be wrapped in its own <ItemGroup> parent element.

Compiler Instructions

Toward the end of the file, you’ll find the instructions for TypeScript compilation within a <Target> element.

<Target Name="BeforeBuild">
 

In it, MSBuild is told to output the complete command sent to the tsc executable file and then execute the command. If the application is in debug mode, MSBuild adds the --sourcemap option to the compiler call so the TypeScript file can be stepped through rather than having to debug the JavaScript and then work backward to the issue in the TypeScript. MSBuild derives the file list sent to tsc by enumerating all the files listed in <TypeScriptCompile> elements. As these instructions target the BeforeBuild step, all TypeScript compilation will occur before any C# or VB.NET compilation.

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
  <TypeScriptSourceMap> --sourcemap</TypeScriptSourceMap>
</PropertyGroup>
 
<Target Name="BeforeBuild">
  <Message Text="Compiling TypeScript files" />
  <Message Text="Executing tsc$(TypeScriptSourceMap)
    @(TypeScriptCompile ->'&quot;%(fullpath)&quot;', ' ')" />
  <Exec Command="tsc$(TypeScriptSourceMap)
    @(TypeScriptCompile ->'&quot;%(fullpath)&quot;', ' ')" />
</Target>
 

Note that the <Exec> element assumes that the directory for the tsc compiler has been added to your PATH. If it hasn’t for some reason, you’ll need to add the full path to the exec command (and then remember to update it whenever you install the latest version of TypeScript).

<Target Name="BeforeBuild">
  <Exec Command="&quot;$(PROGRAMFILES)Microsoft SDKsTypeScript<VERSION> sc&quot; @(TypeScriptCompile ->'&quot;%(fullpath)&quot;', ' ')" />
</Target>

Tweaking Your Existing Project File

If you are adding TypeScript files into an existing project, you need to know that Visual Studio will add information to your MSBuild file listing the TypeScript/JavaScript files and their dependencies, but it will not add the compiler instructions for you. You’ll need to do this yourself:

  1. Add the <Exec> element into your project’s BeforeBuild step.
  2. Add the <TypeScriptSourceMap> element into your project’s debug build PropertyGroup element.

If your project has neither a BeforeBuild step nor a debug build PropertyGroup, add them in their entirety as given in the previous section.

In fact, if you’re in the project file anyway, you might find it useful to refactor it slightly and tell it to compile every TypeScript file in the project.

<ItemGroup>
  <AvailableItemName Include="TypeScriptCompile" />
</ItemGroup>
<ItemGroup>
  <TypeScriptCompile Include="$(ProjectDir)***.ts" />
</ItemGroup>
 

This preemptive inclusion of every .ts file into the TypeScriptCompile group replaces all the <TypeScriptCompile> elements with this single one. You’ll want to remove the others if you go for this approach.

You can also add any of the options seen earlier in the chapter to the compiler call. For example, to generate ECMAScript 5 code, to include your comments in the JavaScript, and so forth, the <Exec> element is the place to do it:

<Exec Command="tsc$(TypeScriptSourceMap) -c --Target ES5 @(TypeScriptCompile ->'&quot;%(fullpath)&quot;', ' ')" />
 

For non-TypeScript-specific projects, you can use the <None> item group to list the .ts files but not include them in releases:

<ItemGroup>
  <Content Include="app.js">
    <DependentUpon>app.ts</DependentUpon>
  </Content>
  <Content Include="second.js">
    <DependentUpon>second.ts</DependentUpon>
  </Content>
  <None Include="second.ts" />
  <None Include="app.ts" />
</ItemGroup>
 

Using this approach requires you to include the AvailableItemName and preemptive TypeScriptCompile elements shown in the earlier code.

The Web Site Project

The preceding approach works for all Visual Studio projects except for one: the Web Site project. With respect to TypeScript, there are two main differences between Web Site projects and Web Application projects.

The first is that TypeScript files aren’t recognized file types for Web Site projects. As Figure 3-2 shows, the Add New Item dialog box for Web Site projects has no TypeScript option.

9781430257257_Fig03-02.jpg

Figure 3-2 .  A Web Site project’s Add New Item dialog box has no TypeScript File option

To add a TypeScript file to a Web Site project, simply add a new text file but give it a .ts extension. Visual Studio picks up the .ts extension and from then on treats it as a TypeScript file (visually at least). The fact that Visual Studio doesn’t think of it as a valid file type for a Web Site project doesn’t matter, because there’s no MSBuild file to update manually anyway.

In fact, this is the main problem with TypeScript in Web Sites projects. They have no MSBuild (.proj) file. Instead, MSBuild is passed the .sln file containing the root directory of the site and from there it generates the .proj file on the fly. All of which means, we can’t add the all-important compiler instructions to the projects’ build instructions in the same way we could with other projects.

Fortunately, there are three workarounds:

  • If possible, install the Web Essentials extension for Visual Studio and make sure the Compile TypeScript on Save option (see Figure 3-1 earlier) is set to True.
  • Write a batch file that calls the TypeScript compiler first and then MSBuild itself on the solution file. For example:
    @echo off
    setLocal EnableDelayedExpansion
    for /f %%a in ('dir /b WebSiteFolder/*.ts') do ( set str=!str! "%%a" )
    tsc !str!
    msbuild WebSiteProject.sln
  • You can get MSBuild to generate a hard copy of the .proj file it creates on the fly when building the project. Open a Visual Studio command prompt window and browse to the folder containing the .sln file for your project. Then run the two following commands:
    > set msbuildemitsolution = 1
    > msbuild <ProjectName>.sln

You’ll find that two .metaproj files have been generated in the same folder as the .sln file: one for the solution as a whole and the other for the Web Site project. You may then be able to add the BeforeBuild step and TypeScript file list to the solution’s .metaproj file and call MSBuild on that.

In Your Web Sites

After your project is compiling your TypeScript to your preference, you still have a couple of points to consider if you’re a web developer.

You can’t embed TypeScript within a web page. TypeScript lives inside its own files. If your current project has web pages with <script> blocks containing actual JavaScript rather than references to files, you’ll need to ask yourself whether there is any way to refactor that JavaScript out to an external file. Chapter 1 provides a very simple example of doing this, so all the event wire-up and scripting is done after the page has loaded. If it’s not possible (perhaps your server-side code injects script into the page), remember that your TypeScript code will still be able to interact with any functions or variables on the page as long as you include ambient declarations for them in your .ts file.

image Note  There’s nearly always a way to separate a web page from its JavaScript. Try googling Unobtrusive JavaScript for hints and tips.

If you’re using the Bundling feature in ASP.NET 4.5’s System.Web.Optimization feature, you may want to use that to gather your compiled JavaScript (not the TypeScript files) into one file rather than using the compiler’s own --out option. Indeed, this approach offers a lot more flexibility if you want to compile several libraries into their own files.

BundleTable.Bundles.Add(
   new ScriptBundle("∼/ts/AllScript.js")
         .IncludeDirectory("∼/ts/", "*.js", true));
 

Remember, however, to reference the bundled JavaScript file in your pages and not the individual JavaScript files.

image Note  You can also find a bundle transform for ASP.NET that takes a TypeScript bundle and compiles it to JavaScript at https://github.com/wouterdevinck/TypeScript-BundleTransform/.

In the Development Life Cycle

Before wrapping up, it’s worth saying that whatever you can do in JavaScript, you can probably also do in TypeScript, and some more on top of that. Declaration files for popular JavaScript libraries are still being written, and the TypeScript project itself is still a way from wrapping up a v1.0 release. By that time, it would not be surprising to see a complete and cogent application development life cycle based solely in HTML and TypeScript. Consider the following:

  • The UI design phase is language-neutral anyway.
  • The Class and Module features in TypeScript allow for more-accurate code design, development, and maintenance already.
  • The TypeScript debugging story is already getting better through the generation of source map files that allow step-through debugging in Visual Studio.
  • Unit testing in TypeScript is already possible with a QUnit declaration file in the works. Other efforts also show that unit testing is possible using Jasmine and Chutzpah. (See the appendix for links.)
  • Including TypeScript files in source control is the same as for any other type of file. Just remember not to check in the JavaScript files that the compiler generates from them. After all, you don’t check in your project’s bin and obj folders, do you? Do you?

Summary

In this chapter, you’ve looked at the more practical issues of TypeScript development. To wit:

  • Compiler options and what they do
  • Where to source and how to write your own declaration files
  • How to incorporate TypeScript compilation into your own Visual Studio projects

I hope that you’ve enjoyed this whistle-stop tour and that you’ll start using TypeScript in earnest. Now really is the best of times to start.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.170.187