Search This Blog

Loading...

Monday, April 11, 2016

TypeScript - How to import other TypeScript files.

As you proceed using TypeScript in large projects, you will end up with multiple typescript files. Then you may find a problem how we can use/import functions/classes from other files.

For example we have following interface in IShape.ts file.

interface IShape {
        x: number;
        y: number;
        height: number;
        width: number;
    }

And in another file Square.ts we have Square class with one of the constructor overloads is accepting that IShape interface as parameter.

class Square {
        public x: number;
        public y: number;
        public height: number;
        public width: number;

        constructor();
        constructor(obj: IShape);
        constructor(obj?: any) {
            this.x = obj && obj.x || 0
            this.y = obj && obj.y || 0
            this.height = obj && obj.height || 0
            this.width = obj && obj.width || 0;
        }

But if you try to compile this, you may get the following error:

Error TS2304 Cannot find name 'IShape'.

Because Square.ts don't know about ISquare.ts. To fix this error, we have to add reference to ISquare.ts file on the top of Square.ts.

/// 

class Square {
        public x: number;
        public y: number;
        public height: number;
        public width: number;

        constructor();
        constructor(obj: IShape);
        constructor(obj?: any) {
            this.x = obj && obj.x || 0
            this.y = obj && obj.y || 0
            this.height = obj && obj.height || 0
            this.width = obj && obj.width || 0;
        }

Now it will compile successfully.

You can add multiple lines of references to add multiple files. Just remember that path is relative to the current file.

Monday, March 28, 2016

TypeScript - Namespaces

TypeScript allows to define namespaces for logical grouping of code. Older versions of typescript use the keyword module while newer version can also used the keyword namespace which can be used in place of module keyword. Lets see namespaces in action in following example.
We have defined a class MySquare in namespace ShapesNamespace.
namespace ShapesNamespace {
    export class MySquare {
        public x: number;
        public y: number;
        public height: number;
        public width: number;
    }
}
Where namespace keyword defined namespace scope. Also notice the keyword export before the keyword class, it will expose this class to become accessible from outside of ShapesNamespace. If you do not add export keyword, you can not access this class outside of ShapesNamespace.
Now let use this class in another namespace.
namespace AnotherNamespace {
    var square1 = new ShapesNamespace.MySquare();
    square1.height = 10;
}
Here we are accessing MySquare class from AnotherNamespace with fully qualified class name. Intellisense will show available items in the code.
Another way to use code from other namespaces is to use aliases. Let see how we can use short names as aliases instead of typing more text in fully qualified names again and again.
namespace AnotherNamespace {
    import MySquareAlias = ShapesNamespace.MySquare;
    var square1 = new MySquareAlias();
    square1.height = 10;
}
We used the import keyword to set alias of ShapesNamespace.MySquare class, when we need to access that class, we can simply use its alias MySquareAlias instead of typing full name. Also intellisense works as expected.

Monday, March 14, 2016

TypeScript - Function / Constructor Overloading

TypeScript supports overloading of functions/constructor, but not in the same way as in .Net. Since JavaScript does not provide support of overloading itself, TypeScript enforces strong typing against the various overload signatures at design time, the function name is the same, but the signature of each overload (i.e. their name and parameters, but not the return type) is different. But we still have to write a generalized function that could work in generated JavaScript code, that is compatible with all overloads signatures, and will have only one function implementation.

This example make it clear:

interface IShape {
        x: number;
        y: number;
        height: number;
        width: number;
    }

    class Square {
        public x: number;
        public y: number;
        public height: number;
        public width: number;

        constructor();
        constructor(obj: IShape);
        constructor(obj?: any) {
            this.x = obj && obj.x || 0
            this.y = obj && obj.y || 0
            this.height = obj && obj.height || 0
            this.width = obj && obj.width || 0;
        }
    }

Square class has total 3 constructor signatures, first one is empty, second contains an object of type implementing interface IShape, and the last overload receives parameter with type any to make it compatible with previous overloads. Also notice that only the last overload function signature could have the actual implementation code.

In the calling stack, it looks at the overload list, and check the first overload to call the function with the provided parameters. If it finds a match, it picks this overload as the correct overload. For this reason, usually overloads should be ordered from most specific to least specific.

You can create objects with this class in the following ways:

    //empty constructor
    var square1: Square;
    square1 = new Square();
    square1.x = 10;
    square1.y = 50;
    square1.height = 100;
    square1.width = 100;
    
    var squareConfig: IShape;
    squareConfig = { x: 10, y: 50, height: 100, width: 100 };

    //contstructor with interface IShap
    var square2: Square;
    square2 = new Square(squareConfig);

    //contstructor with JavaScript object literal
    var square3: Square;
    square3 = new Square({ x: 10, y: 50, height: 100, width: 100 });

If you are working in Visual Studio with TypeScript support enabled, you will get intellicense support, and display avialable overloads similar to this:

Empty Constructor
Constructor with IShape parameter

Following function overloads added to the Square class, having different parameters of basic types.

 SetColor(x:boolean) : void;
 SetColor(x: number): void;
 SetColor(x: string): void;
 SetColor(x: any): void
 {
  if (typeof x == "boolean"){
   alert("Boolean overload is called, use default color value.");
  }
  else if (typeof x == "number") {
   alert("Number overload is called, use color from number value.");
  }
  else if (typeof x == "string") {
   alert("Number overload is called, use color from string value.");
  }
 }

Since we could have only one real function implementation, so inside this function body, we have to check for different types in order to respond with desired behavior.

Visual Studio Intellicense support will display overload options similar to this:

Overload with Boolean type parameter
Overload with Number type parameter
Overload with String type parameter

Above function can be called in the following ways:

    square1.SetColor(true);
    square1.SetColor(255);
    square1.SetColor("#235253");

Wednesday, March 9, 2016

TypeScript: cast HTMLElement - using function document.getElementsByTagName()

While working with document.getElementsByTagName() in TypeScript, I faced problems in casting. Finally I make it work after searcing different sites, and concluded those things in this post that could help someone facing the same problem.

Function document.getElementsByTagName() returns NodeListOf<Element>, Where Element is more generic type extends from type Node. Since TypeScript used lib.d.ts declaration file to recognize native javascript functions and properties, you can find this function's multiple signatures in lib.d.ts with different tag names, each have different return type. For example, document.getElementsByTagName("h1") will return NodeListOf<HTMLHeadingElement>.

Here is the list of some common html elements with corresponding varabiles casts.

 var nodeList1: NodeListOf = document.getElementsByTagName("h1");
 var nodeList2: NodeListOf = document.getElementsByTagName("p");
 var nodeList3: NodeListOf = document.getElementsByTagName("div");
 var nodeList4: NodeListOf = document.getElementsByTagName("a");
 var nodeList5: NodeListOf = document.getElementsByTagName("input");

NodeList is an array-like structure containing multiple items of Node, you can access individual item by passing a particular index. For example, following example will return the first element in the nodelist with tag name h1.

 var var1: HTMLHeadingElement = document.getElementsByTagName("h1")[0];

In inheritance tree of the Document Object Model in lib.d.ts file, you can find that Element extends the Node type, acting as more generic type. Which further extended by HTMLElement, and then all major elements, including in above listing, will extend from HTMLElement.

Same like C# (or other Object-Oriented Languages), you have to take care of proper inheritance structure while working with elements casting. Consider these variables:

 var var1: Element = document.getElementsByTagName("h1")[0];
 var var2: HTMLElement = document.getElementsByTagName("h1")[0];
 var var3: HTMLHeadingElement = document.getElementsByTagName("h1")[0];

If you try to assign var2 to var1:

    var1 = var2;

it will be ok, since HTMLElement extends from Element. But if you try to assign var1 to var2:

    var2 = var1;

you will get compile error as below:

 TS2322 (Error Code)
 Type 'Element' is not assignable to type 'HTMLElement'. Property 'accessKey' is missing in type 'Element'.
 
 //or in generic form, if you try similar thing with other elements.
 Type 'x' is not assignable to type 'y'. Property 'z' is missing in type 'x'.

Because compiler is looking for target type's properties, if any property is missing in source type, it will prompt similar error message showing the name of the missing property.

The function document.getElementsByTagName() always returns NodeListOf<T>, If you want to work on array, you can cast this to array in the following statement:

 var nodeList: HTMLElement[] = (<HTMLElement[]> <any> document.getElementsByTagName("h1"));

This returns all h1 elements in variable nodeList, the array of HTMLElement.

Or you can also iterate through nodeList array items in for loop. In the following example, it will iterate through all items in nodeList and add these items to a new array.

 var nodeList: NodeListOf = document.getElementsByTagName("h1");
 var elementList : Array = [];

 if (nodeList) {
  for (let i = 0; i < nodeList.length; i++) {
   
   let node : Node = nodeList[i];
   elementList.push(node as HTMLElement);
  }
 }

References:

http://www.typescriptlang.org

Monday, March 7, 2016

Getting started with TypeScript in ASP.Net MVC Project

Microsoft introduced TypeScript, a typed superset for ECMAScript 2015 (ES2015) syntax that compiles down into plain JavaScript, it provides the ability to translate/compile its features into ECMAScript 3 or 5. Since JavaScript is just a scripting language and not have designed as a programming language for big web applications. It does not provide structured coding pillars likes classes, interfaces, modules/namespaces. So there comes TypeScript, both language and compiler is open-sourced under Apache 2.0 license. TypeScript compiler performs only file-local transformations for TypeScript code files, and does not re-order the code segments (like variable declarations).

It focuses on providing useful tools for building large scale applications by implementing features, such as classes, interfaces, OOPs, modules and much more. TypeScript file extension is .ts and when compiled, it produces plain JavaScript output file with .js extension. In this tutorial, we will try to get started with TypeScript in Visual Studio IDE, by first installing its plugin for Visual Studio and then will write small TypeScript code to make sure we have installed TypeScript successfully and ready to write real code for our applications.

There are two main ways to install the TypeScript

Since we are using Visual Studio as our IDE, I will follow second option in this post. Now I assumed that you have Visual Studio setup with required TypeScript plugin and also Web Essentials. Create a sample ASP.Net MVC Web Application project, and run it to see every thing is working fine.

Incorporate TypeScript into Visual Studio projects

Lets add our first TypeScript in this project, create a new file with FileTemplate as TypeScript File, named file1.ts, all typescript files have extension of .ts. When you compiled project next time, if every thing is properly setup it will be build successfully. But you might not get the generated output javascript file, if so, you might have to follow these instructions.

First right click on project file in Solution Explorer, and click Unload Project.

Right click on project file in Solution Explorer, and click Edit {ProjectName}.csproj.

In xml, look for ItemGroup tags, and add a new ItemGroup tag for typescript files we want to use. So in this example, since we only have one typescipt file file1.ts, you should have something similar to this.

  
     <ItemGroup>
    <Content Include="file.js">
      <DependentUpon>file.ts</DependentUpon>
    </Content>
  </ItemGroup>
  <ItemGroup>
    <TypeScriptCompile Include="file1.ts" />
  </ItemGroup>
  <Target Name="BeforeBuild">
    <Exec Command="&quot;$(PROGRAMFILES)\Microsoft SDKs\TypeScript\0.8.0.0\tsc&quot;@(TypeScriptCompile ->'"%fullpath)"', ' ')" />
  </Target>

After finished adding support for typescript files in project file's xml, right click on the project file and click Reload Project.

Try rebuild the project, it should be completed successfully.

Now our project is ready for typescript. Remember that typescript is only syntactic sugar, and has value only till compile time. On successfully compilation, it generates the output javascript file or real use in the same directory where current typescript file is placed. So it means, finally you have to use the generated javascript file in html as we are using regular javascript. Lets see typescript in action.

Open BundleConfig class in App_Start folder, and add the generated javascript file path in RegisterBundles function as follows:

	public static void RegisterBundles(BundleCollection bundles)
	{
		...code removed for clearity	

		bundles.Add(new ScriptBundle("~/bundles/MyJavaScript").Include(
								"~/MyTypeScripts/file1.js"
								));
	}

We have to add the file path for generated javascript file, not the typescript file. For this example, I am adding a simple button in Index view of HomeController. But before this you have to add the script reference in shared _Layout.cshtml in head section:

    @Scripts.Render("~/bundles/MyJavaScript")
At the bottom of the Index view of HomeController, I added the following script tag:
    // A $( document ).ready() block.
    $(document).ready(function () {

        initMyScript();
    });

This will call initMyScript() javascript function on document load. So far we have not written this function in javascript or typescript. Lets write first typescript code in file.ts. Since this post is limited only for getting started typescript in Visual Studio, so I used only simple script for clarity.

function initMyScript(): void {

    document.getElementById("aLink").addEventListener("click", setParagraphText);
}

function setParagraphText():void {

    var msg: string = "link is clicked";
    document.getElementById("pTest").innerHTML = msg;
}

This typescript defines two functions, initMyScript() function, which is called on document load, and attach an event listener for click event of element aLink. The setParagraphText() event handler sets the innerHTML of element pTest by a simple string variable.

One successfully compilation of above typescript, it will generate the corresponding javascript in file.js:

function initMyScript() {
    document.getElementById("aLink").addEventListener("click", setParagraphText);
}
function setParagraphText() {
    var msg = "link is clicked2";
    document.getElementById("pTest").innerHTML = msg;
}
//# sourceMappingURL=file1.js.map
And this is simple html for elements used in above script:
<div class="col-md-4">
        <p><a id="aLink" class="btn btn-default" >Click Me</a></p>

        <p id="pTest">This content will be replaced.</p>
</div>

Thats it. Now run your project to see TypeScript in action. I discussed here only the minimum steps required to incorporate TypeScript in Visual Studio MVC Projects. You can continue playing with TypeScript by adding advanced features.

References:

http://www.typescriptlang.org