What does the world look like in TypeScript? What do you gain or lose by coding in TypeScript versus ES6?
In case you've been pondering this question, today we'll do a deep dive to help you sort the answer. The best way to illustrate this is with code, so let's dive right into it. In this article, we'll convert one of Kendo UI's sample apps - the Layout Diagram App. I chose this example because it's stuffed with Kendo UI controls of all sorts. Since many of us develop with AngularJS, we'll go ahead and refactor it from its jQuery implementation to Angular as well (if you don't use Angular, the example is still relevant, simply ignore the specific Angular bits).
You'll need to download all the TypeScript definitions. This contains the definitions for all of the JavaScript libraries that are in our project - AngularJS, lodash, Kendo UI, etc. You can think of the TSD command-line tool as the equivalent to other package dependency tools like Nuget, Bower, npm, etc.
To install TSD, you'll need to have Node.js/npm installed:
You can now search and install other TypeScript packages. Tsd currently has definitions for both client-side (browser) JavaScript libraries and server side JavaScript libraries as well.
For example:
...yields these results (list shortened for brevity):
You can then install the specific ones you need.
You may notice how TypeScript definition files are suffixed with "d.ts" such as angular.d.ts for the AngularJS TypeScript defiinition file. Passing the --save
option when installing a definition file will create a tsd.d.ts file if it doesn't already exists and add an entry in this file for each of our project's TypeScript definition dependencies.
Here's the folder sturcture after running this command:
Below you will notice how a line has been added for our Angular tsd dependency now along with all the other tsd dependencies for our app/project in tsd.d.ts.
You may notice that the Kendo UI tsd is in the list. Sometimes JavaScript libraries that we download such as Kendo UI, angular-ui-router, and others include the tsd with them. In these cases we can just open up the tsd.d.ts file and reference them directly to where they are in our app/project directory structure (using a relative path).
As I mentioned earlier, for this article, we'll refactor the Kendo UI Diagram Sample Application to TypeScript using AngularJS. This is a good sample to work with because it has a long list of Kendo UI widgets within sample app allowing us to illustrate working with a vast amount of Kendo UI widgets with TypeScript and AngularJS.
In TypeScript there is the notion of static typing your data types, and from what I've seen, most teams usually just fully qualify whatever they're typing. However for this article we'll go ahead and alias most of Kendo UI typing's so that they're shorter and cleaner, for the sake of brevity of this article. Again, you can skip the aliasing of the namespaces step here and fully qualify everything if you wanted to.
For example, here we are initializing an ObservableArray
using a fully qualified namespace:
While, below, we are initializing an ObserverableArray
using an aliased namespace:
Next, let's go ahead and address separation of concerns. We'll separate the view (presentation) from the any logic and place that logic in the view model along with any other view-like responsibilities such as widget initializing and data-binding as well.
As a general best practice, I like to create an interface for every Angular Controller/ViewModel and place it in the same file as the implementation of the Controller. Why? Here are a few important reasons.
ng.IScope
($scope
)Here is the IDiagramController interface (diagram.controller.ts):
Now we can setup the class/implementation for IDiagramController
that we'll use as the Controller/ViewModel for our View. Notice, below, that our class or implementation is where we register our DiagramController with Angular. I've also recommended this approach with Angular 1.x because this will play nicely if any whenever you decide to upgrade to Angular v2.
The benefit of TypeScript is that it offers type safety for everything that is typed along with IDE support to identify development time and build time errors. When fully typing with TypeScript, you will get development-time or build-time errors when your types are mixed or inconsistent as you would with any statically typed language such as C#, Java, C++, etc.
For instance, if you are working in Visual Studio Code, you will notice that you immediately get warnings that the interface has not been properly implemented and if we were to transpile the TypeScript get build errors. This is effectively the same if we were mix types as well (e.g. declare something as a number then try to save a string value into it).
Below TypeScript is able to infer that myArray
is of type ObservableArray
from the declaration. However, we then try to set myArray
to an ObservableObject
m TypeScript immediately indicates something is wrong here.
Let's look some examples of how the code gets refactored to support the new architecture. First, the jQuery and JavaScript version:
Compare that to the version written with TypeScript and AngularJS (diagram.controller.ts):
Here we'll refactor the ShapeProperties
change event, which synchronizes the selected object on the design surface when a change is made to one of it's properties such as color, stroke, etc.
First, the jQuery and JavaScript version:
Now let's look at the TypeScript & AngularJS version - diagram.html:
Looking at diagram.controller.ts, you'll notice that we no longer have to scrape UI controls using jQuery selectors thanks to Angular's MVVM goodness. We are now binding directly to our ViewModel from the View:
You may notice that we get to use new features of ES6/ECMA6 in TypeScript. For instance, we use arrow functions in the forEach
method above (aka fat arrows and lambdas).
With the recent release of TypeScript v1.7x, we even get to develop with new features of ES7/ECMA7 today which all transpiles down to a compatible version that will run in today's browsers, even if they don't yet support ES6 or ES7.
In addition, we get real Intellisense for even Kendo UI types because we declared selectedShape
and typed it to the Kendo UI Shape
type.
Obviously this would be the same for all library types whose TSDs you imported, including jQuery, Angular and lodash.
In addition, we can now do a true "Find all References" or "Find all Usages". For instance, you can do a "Find All References" for this.selectedShape
on our ViewModel or Angular Controller. If this was something that was used project-wide, our results list would span the entire project as well.
If you do this in Visual Studio Code, it will open up the "peek" view and give you a list of all usages of this.selectedShape
on the right. You can navigate to each occurrence by clicking on them and as you navigate through the list the view on the right will automatically scroll to that occurrence.
Other exciting TypeScript features include:
It's worth noting that these features are not exclusive to Visual Studio Code. They are available on most IDEs that officially support TypeScript. TypeScript brings a lot of power to increase your development productivity in terms of providing a robust development and build time experience for JavaScript.
I've uploaded the completed sample used in this article of the refactored Kendo UI Diagram Application built with TypeScript and AngularJS to GitHub. You can find it at https://github.com/lelong37/kendo-angular-typed. I've also deployed the completed, refactored application here.
In upcoming articles I plan to cover TypeScript with Kendo and Angular 2 and TypeScript with NativeScript. Be sure to share any feedback with me via Twitter @lelong37 or in the comments.
Happy Coding!