You are here, that means you have successfully completed Day1-part 1 of the series. In part 2 we explore some more fundamental concepts of TypeScript.
Complete Series
- Day 1 – Part 1
- Day 1 – Part 2
- Day 2
- Day 3
- Day 4 – Part 1
- Day 4 - Execution trick
- Day 4 – Part 2
- Day 4 – Part 3
- Day 5 (Coming soon)
- Day 6 (Coming soon)
- Day 7 (Coming soon)
- Day 8 (Coming soon)
- Day 9 (Coming soon)
- Day 10 (Coming soon)
Contents
Demo 8 – Classes
TypeScript makes OOP possible for us. It have inbuilt support for Classes and most of the OOP features. Let’s do a quick demo.
Example on class with simple variables and functions
Try the below code.
class Customer{
CustomerName:String;
Address:String;
Save():void{
console.log('Customer Saved:'+
this.CustomerName+"-"+this.Address);
}
}
let c:Customer=new Customer();
c.CustomerName="Customer1";
c.Address="Address1"
c.Save();
let c1:Customer=new Customer();
c1.CustomerName="Customer1";
c1.Address="Address1"
c1.Save();
Compile the code and execute the generate JavaScript code using node.
The output will be as follows.
Example on Access modifiers
We can easily decide what members of the class will be exposed outside the class and what members will not be with the help of access modifiers. The default access modifier is "public".
class Customer{
private CustomerName:String;
public Address:String;
Save():void{
console.log('Thanks');
}
}
let c:Customer=new Customer();
console.log(c.Address);
console.log(c.CustomerName);
Compilation will issue following error.
Example on class with properties
Properties are simply wrapper over variables with getter and setter logic.
Try the below code.
class Customer{
private _age:number;
get age():number {
return this._age;
}
set age(theAge:number) {
console.log('new age received '+theAge)
if(theAge<0 || theAge>100){
throw "Invalid Age";
}
this._age = theAge;
console.log('new age is set '+theAge)
}
}
let c:Customer=new Customer();
c.age=55;
c.age=-65;
Compile it using the "tsc classExample.ts" command and you will get following error.
TypeScript is configured to generate ES3 code by default.
To support properties, at minimum, "ES5" is required.
Recompile the same code using the "target" flag as follows.
tsc classExample.ts --target es5
This time it will compile. Now execute generated JS file using node. The output will be as follows.
As you can see, the first one successfully worked whereas the second one generated a run time error.
Example on class with constructor
Constructor is a known feature for every Object Oriented Programmer.
It’s a special type of method which have same name as class name and which will be invoked automatically when the class get initialized.
Let’s have a quick demo.
class Customer{
CustomerName:String;
Address:String;
constructor(){
console.log('Customer new object created');
}
}
let c:Customer=new Customer();
let c1:Customer=new Customer();
Compile and execute, and the output will be as follows.
Example on class with short hand properties
If you are an Object oriented programmer, you must have done something like the below.
class Customer{
CustomerName:String;
Address:String;
constructor(n,a){
this.CustomerName=n;
this.Address=a;
}
}
let c:Customer=new Customer("Sukesh","Mumbai");
let c1:Customer=new Customer("Ganesh","Bengalore");
console.log(c.CustomerName + "-"+c.Address);
console.log(c1.CustomerName + "-"+c1.Address);
Inside the constructor, Class members are simply initialized with constructor parameters.
Compile and execute and the output will be simply as follows.
The same code can be re-written with the help of shorthand properties as follows.
class Customer{
constructor(public CustomerName:string;
public Address:string;){
}
}
let c:Customer=new Customer("Sukesh","Mumbai");
let c1:Customer=new Customer("Ganesh", "Bangalore");
console.log(c.CustomerName + "-"+c.Address);
console.log(c1.CustomerName + "-"+c1.Address);
Compiling and executing the above code will generate the exactly same out.
Just specifying access modifier before parameter name in the constructor and it became properties.
The "public" keyword indicates that they are public properties, if required we can make it private too.
Example on Inheritance
Without inheritance, Object oriented programming is incomplete.
Let’s do a quick demo. Check the below code.
class Customer{
CustomerName:string;
protected Address:string;
}
class GoldCustomer extends Customer{
Discount:number;
constructor(a:string){
super();
this.Address=a;
}
}
let c:GoldCustomer=new GoldCustomer("Mumbai");
c.CustomerName="Sukesh";
c.Discount=100;
console.log("CustomerName:"+c.CustomerName);
console.log("Discount:"+c.Discount);
console.log("Address:"+c.Address);
Compile it and you will get following error.
Other than public and private, protected is the third access modifier.
Protected variables will be accessible only in the defined class and the base class.
Change access modifier of "Address" to "public" and try again. (You can even try the same example, by not specifying any access modifier because the default access modifier is public.)
This time it will compile. Execute it using node and the output will be as follows.
Demo 9 – Template strings
String concatenation is much easier in TypeScript with the help of Template strings.
It will be done via a special symbol called tick (`).
Check the below code.
let a:string="ValueA";
let b:string="ValueB";
let c:number=55;
let text:string=`Value of a+b is ${a}${b}
Value of c is ${c}`
console.log(text);
Note:
For representing string symbol used is tick (`). Located just below the escape key on the keyboard.
Don’t get confused with single quote (‘).
Compile and execute. The output will be as follows.
Demo 10 –Arrow functions
Passing functions as parameter is a very common practice in JavaScript world.
TypeScript provides a very convenient way to define stateless functions called Arrow functions. A stateless function is a function which won’t have its own state, it will share the scope of the outer function where it’s defined. In simple words, "this" keyword inside arrow functions refers to the lexical scope (outer functions scope).
Still confused? Let’s do a very quick demo using JavaScript’s "setTimeout" method. The setTimeout function simply expects another method as an argument and call that function after a specified number of milliseconds.
Let’s do it first using a traditional approach.
Create a new TypeScript file called "arrowFunctions.ts" with the following code;
class Customer{
IsSaved:boolean=false;
Save(f):void {
setTimeout(function() {
this.IsSaved=true;
f();
}, 100);
}
}
let e:Customer=new Customer();
e.Save(function(){
console.log(e.IsSaved);
});
Compile and execute it and you will see "false" in the output. As I said before "this" refers to the instance of anonymous function passed to "serTimeout" function. Such problems can be solved easily using arrow functions.
Rewrite the same code using arrow functions as follows.
class Customer{
IsSaved:boolean=false;
Save(f):void {
setTimeout(()=>{
this.IsSaved=true;
f();
}, 100);
}
}
let e:Customer=new Customer();
e.Save(function(){
console.log(e.IsSaved);
});
Compile and execute it. Output will be "true".
Arrow function syntax can also be used in function declaration to make it type safe.
Check the below code snippet.
...
Save(f:()=>void):void {
…
}
It means, the save function expects a parameter of type function (with no parameter and void return type).
Demo 11 – Interfaces and Abstract classes
OOP is not complete without interfaces and abstract classes.
Create a new TypeScript file called "looselyCouping.ts" with the following code.
interface ILogger{
LogError(e:string):void;
SendEmailLog(s:string):boolean;
}
abstract class AbstractServiceInvoker{
Save(){
let ServiceURL:string=this.GetServiceURL();
let Credentials="UserName:abcd,password:123";
let data:string=this.getData();
let Result:number=5;
this.setData(Result);
}
abstract GetServiceURL():string;
abstract getData():string;
abstract setData(n:number):void;
}
As you can see we have one interface ILogger and one abstract classes AbstractServiceInvoker.
Now first let’s create an implementation of interface.
class Logger implements ILogger{
LogError(e:string):void{
console.log(`error ${e} logged`);
}
SendEmailLog(s:string):boolean{
console.log(`message ${s} sent in email`);
return true;
}
}
It is must to define both LogError and SendEmailLog method in Logger class or else compile will issue error.
Now let’s look at the abstract class implementation.
class CustomerService extends AbstractServiceInvoker{
GetServiceURL():string{
return "http://someService.com/abcd"
}
getData():string{
return "{CustomerName:'a',Address:'b'}"
}
setData(n:number):void{
console.log(`Success:${n}`)
}
}
It is a must to define all abstract methods in derived classes or the compiler will issue an error.
Demo 12 – Destructuring
Using this feature we can extract values from an array into individual variables in a very easy way.
It also allows us to extract property values of an object into independent variables easily.
Create a new TypeScript file called "Destructuring.ts".
First let’s try with Object Destructuring
Check the below code
var a={CustomerName:'A',Address:'b'};
var {CustomerName,Address}=a;
console.log(`${CustomerName}-${Address}`);
Compile it and execute it.
It will simply display "a-b" in output.
Now let’s try Array Destructuring.
Check the below code.
var myArray=[1,2,3,4];
var [v1,v2,v3]=myArray;
console.log(`${v1}-${v2}-${v3}`);
Compile it and execute it.
It will simply display "1-2-3" in output.
Demo 13 – Namespaces
In Part 1 we have seen JavaScript Namespaces in action. We understood the importance of it along with the logical way of implementing it.
Now, in TypeScript no need to worry about writing IIFE or any other logic for achieving Namespaces. We have inbuilt support for Namespaces here.
Check the below code.
namespace MathsExample{
export function add(x, y) {
console.log(x+y);
}
function sub(x, y) {
console.log(x-y);
}
export class MyClass{}
}
MathsExample.add(1,2);
In the above example only "add" function and "MyClass" class will be available outside the Namespace.
If you compile the code, you will find a custom IIFE logic generated in JavaScript,
var MathsExample;
(function (MathsExample) {
function add(x, y) {
console.log(x + y);
}
MathsExample.add = add;
function sub(x, y) {
console.log(x - y);
}
var MyClass = (function () {
function MyClass() {
}
return MyClass;
}());
MathsExample.MyClass = MyClass;
})(MathsExample || (MathsExample = {}));
MathsExample.add(1, 2);
It’s a simple JavaScript code
. For testing, open command prompt and try following command.
node tsNameSpaceExample.js
Demo 14 – Modules
Just like Namespace, TypeScript have readymade support for Modules.
When a TypeScript file contains at least one global member with export keyword that file will become a TypeScript module. Module name will be same as file name but without extension.
TypeScript compiler will generate the output in "CommonJS" format. It is the default module formatter.
Let’s look at an example.
Step 1 – Setup Folder
Create a new folder called "TsModuleSample" and create two TypeScript files inside it. Reusable.ts and Index.ts
Step 2 – Define Reusable.ts
Put the following code inside Reusable.ts
function Add(...values:Array<number>):number{
let sum:number=0;
values.forEach(e=>{
sum+=e;
})
return sum;
}
export function AddTwoNumbers(x:number,y:number){
return Add(x,y);
}
Step 3 – Define Index.ts
Put the following code inside Index.ts
import {AddTwoNumbers} from "./reusable"
console.log(AddTwoNumbers(11,12));
Note: If TypeScript file name is "A.ts" or "a.ts", Typescript module name will be "a".
Step 4 – Compile it
Now this is where the fun is. Compile index.ts. It will automatically compile Reusable.ts
tsc index.ts
Open the "reusable.js" file. It will be as below.
"use strict";
exports.__esModule = true;
function Add() {
var values = [];
for (var _i = 0; _i < arguments.length; _i++) {
values[_i] = arguments[_i];
}
var sum = 0;
values.forEach(function (e) {
sum += e;
});
return sum;
}
function AddTwoNumbers(x, y) {
return Add(x, y);
}
exports.AddTwoNumbers = AddTwoNumbers;
The above code is following "CommonJS" module format.
Note: By default, TypeScript is configured to generate "CommonJS" code. Later we will see how this configuration can be changed.
Step 4 – Execute it
Execute generated "index.js" using node command.
node index.js
Output will be "23".
You may be wondering how execution was successful without Module Loader.
Node is perfectly comfortable with "CommonJS" module formatter. It understands it well.
If Module formatter is changed to AMD or something else, we won’t be able to execute it without Module loader.
Demo 15 – TypeScript Configuration
TypeScript compiler can be configured as per our need.
Step 1 – Create configuration file
To get started create a new folder called "TSConfigSample" and create a new file inside it as "tsconfig.json". Having this file in the directory is an indication that it’s a root directory of our TypeScript source project.
Step 2 – Create a sample File
Create a new TypeScript file "configTest.ts". Put the following code inside it.
class Customer{
Save():void{
console.log("Thanks");
}
}
Step 3 – Compile
Open command prompt, navigate to "TSConfigSample" folder and simply try following command.
tsc
When tsc is used without file name, it will compile all the files in the current folder.
You will get a very strange error.
Put the following line in the tsConfig.json file and repeat the compilation process.
{}
Now compile will work and output file will look like following.
var Customer = (function () {
function Customer() {
}
Customer.prototype.Save = function () {
console.log("Thanks");
};
return Customer;
}());
Step 4 – Change the configuration
By default, TypeScript is configured to generate "ES5" code. It can be set to ES2015. To do that put the following content inside the "tsConfig.json" file.
{
"compilerOptions": {
"target": "es2015"
}
}
Now compile it again. (Make sure to use "tsc" instead of "tsc filename.ts".
This time output file will be as follows.
class Customer {
Save() {
console.log("Thanks");
}
}
Explore more compiler options
module
This option makes us decide the module formatter. The default option is "CommonJS" but can be changed to "AMD" or other available options as well.
target
What version of target file we are expecting. Default is "ES3" but can be set it to a higher version.
noEmitOnError
Default value is false. If it is set to true, TypeScript won’t generate target JS file when there is a compile error.
allowUnreachableCode
Default is false. When set to true don’t report error on unreachable code.
Check the below code.
class Customer{
Save():boolean{
if(true){
return true;
}
return false;
}
}
Above code fail to compile if "allowUnreachableCode" is set explicitly set to true
noImplicitAny
Default value is false. When compiler cannot infer the variable type based on how it's used, the compiler treat type as "any". For instance, check the below code.
Save(s):void{
console.log(s+1);
}
In above situation parameter type is not mentioned. "Type inference" decide the datatype based on the first assignment and in the above condition, first assignment happens at run time. Hence datatype will be set to "any"
Setting "noImplicitAny" to true forces us to specify datatype. Whenever a situation such as the above occur simply error will be thrown.
suppressImplicitAnyIndexErrors
Have you ever heard of bracket notation in JavaScript world? Check the below JavaScript code.
var a={"key1": "value1", "key2": "value2"};
var myKey= "key1";
console.log(a[myKey]);
The above JavaScript code is valid JavaScript code.
Now let’s try the below TypeScript code having "noImplicitAny" set to true.
class Customer{
CustomerName:string;
Address:string;
Save():boolean{
if(true){
return true;
}
return false;
}
}
let c:Customer=new Customer();
c.CustomerName="Sukesh Marla";
c.Address="Mumbai";
let key: string = 'CustomerName';
console.log(c[key]);
Compile it and a strange compile error will occur.
configTest.ts(23,27): error TS7017: Element implicitly has an 'any' type because type '{ firstKey: string; secondKey: string; thirdKey: string; }' has no index signature.
The error is obvious, TypeScript compile is unable to infer the datatype of "c[key]" hence it will treated as "any".
Setting "suppressImplicitAnyIndexErrors" to true will simply ignore "noImplicitAny" for index based access.
sourceMap
The default value is false. If set to true, on compilation will generate one additional file with an extension of ".js.map". Having these files let external tools such as Visual Studio, chrome developer tools etc. debug TypeScript instead of JavaScript at runtime.
We will see demo when working with Angular.
removeComments
Default value is false. If set to true, the compiler wont emit comments in the generate JavaScript file.
experimentalDecorators
TypeScript have a feature called Decorator which will be used to add additional information to the classes, methods, properties etc.
If you are from C# background them they are similar to Attributes in C# and if you are from Java background then they are similar to Annotations.
In TypeScript Decorators can be attached to a class/method/property/variable/parameter etc.
Syntax is very simple. Let's say we have a class decorator "myClassDecorator", a function decorator "myFunctionDecorator", a Property decorator "myPropertyDecorator" and a parameter decorator "myParameterDecorator". A code snippet for applying them will be as follows.
@myClassDecorator()
class Customer{
@myPropertyDecorator()customerName:string;
constructor(@myParameterDecorator()s:string){
}
@myFunctionDecorator()
Save():void{
}
}
Note: We won’t create custom decorators in this course, it will go beyond the scope of this series. We will concentrate more towards Angular. But definitely we will use lot of readymade Decorators throughout the course.
Decorators are currently experimental features in TypeScript. It can be changed in the later version of TypeScript. In order to use Decorators in the project we need to set "experimentalDecorators" to true in the "tsconfig.json" file.
In order to create an Angular application, setting this flag to true is a must. With the help of decorators we add metadata to our classes/functions/properties etc. Angular uses this metadata later in execution state.
emitDecoratorMetadata
In order to work with Decorators we also need to set this flag to true. It enforces Typescript compiler to save type information with metadata.
The decorator is a feature which will be useful when working with third party frameworks such as Angular. We will notice many frameworks in future leveraging decorators. With the help of decorators we will provide some additional metadata to our methods/classes/properties etc. which will be used by frameworks (or some automation code) at run time.
Just to put up together, "experimentalDecorators" enables us to use Decorators in our TypeScript application whereas "emitDecoratorMetadata" simply adds type information to metadata generated because of Decorator.
moduleResolution
In one of our previous demo we spoke about TypeScript modules.
"moduleResolution" is the process compiler uses to locate the file which represents the imported module.
It can be set to one of two possible values – classic and node.
When module is set to "AMD", "System" or "ES2015" default value for "moduleResolution" will be "classic". In all other situations the default value will be "node"
Import statement can be written in two different ways.
- Relative import – the one start with either / or. / or ../
Example is, Import {a} from "./mymodule"
- Non-relative import – import which won’t start with any of the above symbol.
Example is, Import {a} from "mymodule"
Let’s assume that there is a TypeScript file created in following location.
"H:\Day 1 Source code\TS Examples\TSConfigSample"
It contains the following two import statements
Import {a} from "./mymodule"
Import {b} from "mymodule2"
Classic
The first import will result in the following lookups.
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule.d.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule.ts
The second import will result in the following lookups.
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule2.d.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule2.ts
- H:\Day 1 Source code\TS Examples\mymodule2.d.ts
- H:\Day 1 Source code\TS Examples\mymodule2.ts
- H:\Day 1 Source code\mymodule2.d.ts
- H:\Day 1 Source code\mymodule2.ts
- H:\mymodule2.d.ts
- H:\mymodule2.ts
Node
The first import will result in the following lookups.
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule.d.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule\somemainfile
Search number 3 will only happen,
- there is a folder called mymodule inside current folder
- my module folder contains package.json
- package.json will have types property set to some "somemainfile.ts" or "somemainfile.d.ts" file.
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule\Index.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule\Index.d.ts
The second import will result in the following lookups
- H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\mymodule2.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\mymodule2.d.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\somemainfile
(same package.json and types concept discussed in first import) - H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\mymodule2\index.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\mymodule2\index.d.ts
- H:\Day 1 Source code\TS Examples\node_modules\mymodule2.ts
- H:\Day 1 Source code\TS Examples\node_modules\mymodule2.d.ts
- H:\Day 1 Source code\TS Examples\node_modules\somemainfile
(same package.json and types concept discussed in first import) - H:\Day 1 Source code\TS Examples\node_modules\mymodule2\index.ts
- H:\Day 1 Source code\TS Examples\node_modules\mymodule2\index.d.ts
- H:\Day 1 Source code\node_modules\mymodule2.ts
- H:\Day 1 Source code\node_modules\mymodule2.d.ts
- H:\Day 1 Source code\node_modules\somemainfile
(same package.json and types concept discussed in first import) - H:\Day 1 Source code\node_modules\mymodule2\index.ts
- H:\Day 1 Source code\node_modules\mymodule2\index.d.ts
- H:\node_modules\mymodule2.ts
- H:\node_modules\mymodule2.d.ts
- H:\node_modules\somemainfile
(same package.json and types concept discussed in first import) - H:\node_modules\mymodule2\index.ts
- H:\node_modules\mymodule2\index.d.ts
lib
So far we have not seen the place where our global node modules get installed. It’s the following folder.
%AppData%\npm\node_modules
Inside this folder you will find one folder for each global node module you installed.
Open the "TypeScript" folder. There is one folder called "lib" which contains lot of TypeScript files.
If you check the name of those files, mostly all of them follow a common naming convention. All of them are named as "*.d.ts".
"d.ts" file are called Type definition files.
Type definition files are TypeScript files (named as "something.d.ts"), which contain declarations of constructs available in a particular library or framework. For instance, Type definition files of jQuery contains declaration for jQuery functions.
The simplest definition will be, TypeScript definition file is the way to teach new things to TypeScript compiler.
Files located inside the "lib" folder contain declarations for common JavaScript constructs.
Example they have declaration for "console.log", "document", "Array", "alert" etc.
Based on the declarations given on these definition files TypeScript will check our code and confirm if we have done any mistake. For example, "alert" is declared as follow inside Type definition file.
declare function alert(message?: any): void;
When we try to invoke alert function with more than one parameter Typescript compiler will issue error because based on declaration it can accept only one parameter.
There are two very important files inside the "lib" folder. They are "lib.d.ts" which contains declaration of constructs given by "ES5" and "lib.es6.d.ts" which contain declarations of constructs present in ES2015.
(If you check both the files, you will notice some similar declarations inside. For instance "alert" will be defined exactly as above in both the files.)
Based on the target we set, one of these files will be automatically included in compilation process. These files simply let us write type safe code.
Now let's say we have a scenario, where we want our target to be "ES5" but we want to use "ES2015" features. As soon we set target to "ES5", compiler will start issuing error if we have used any ES2015 functions or classes.
In such situations we will override the default inclusion by using "lib" option. It will let us manually decide what libraries we want include.
Example, Check the below "tsconfig.json" file.
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom","es2015"
]
}
}
It will simply include two library files.
- lib.dom.d.ts – which will let us use common DOM related constructs such as "console", "document", "window" etc.
- lib.es2015.core.d.ts – which will let us use constructs present in ES2015 runtime. Example we can use ES2015 promises.
Note:
- ES2015 is simply a new name for ES6 so don’t get confused between them.
- lib.es6.d.ts is very different from lib.es2015.core.d.ts.
- First one is the default inclusion when target is "ES2015". It contains declarations for almost every construct in JavaScript. It include core constructs (such as Array, Boolean etc.) and DOM constructs (such as document, window, console etc.).
- Second one only contain declarations for core constructs of JavaScript and that’s the reason for DOM construct separate library need to be included.
Demo 16 – Working with third party JavaScript libraries
We already spoke about default libraries get loaded by TypeScript compiler based on configuration settings. These libraries are simple Type Definition files containing declarations for the core JavaScript constructs. The TypeScript compiler confirms the validity of our code based on these files only. It simply issues an error if any of the construct is used an invalid way.
Now the question is, how to deal with third party JavaScript libraries in TypeScript? For instance, let's say we want to use "jQuery" functions in TypeScript. TypeScript will not recognize "$" or any other functions in "jQuery". Using them will end up into compile errors.
The solution is Type definition files. Along with the JavaScript library files we have to download respective type definition files as well.
Let’s do the step by step demo.
Step 1 – Setup folder
Create a new folder "TypeDefinationExample" and create a new TypeScript file inside it called "jQuerySample.ts"
Step 2 – Download library and Type Definition
Open command prompt. Navigate to the above folder and download jQuery using "npm" as follows.
npm install jquery
Note: npm commands are case sensitive. Please make sure "jquery" is written as it is.
The above command will download the jQuery library and place it inside node_modules folder.
Next download the type definition files using following command.
npm install @types/jquery
Step 3 – Write the code
Open "jQuerySample.ts" file and write down below code.
import * as $ from "jquery"
$(document).ready(()=>{
$('#MyElement').html("<b>Hello World</b>");
});
(Don’t worry about the above jQuery Code. Our target will be Angular not jQueryJ. Above code will find an HTML element with Id ‘MyElement’ and set its inner HTML to “<b>Hello World</b>”)
Step 4 – Test the code
Write following command in the command prompt.
tsc jQuerySample.ts
It will result in the following error.
Step 5 – Solve the bug
The latest version of jQuery is based on ES2015 specification. It means we want ES2015 library to be included while compiling. It can be done in two days.
- Create tsConfig.json file and use "lib" option with "es2015" and "dom" and then compile it using tsc
- Use the "lib" compiler option in command prompt itself.
For our example, let’s use the second approach.
tsc jQuerySample.ts --lib es2015,dom
Now it will successfully compile.
Step 6 – Create Source File
Create new HTML file called “Test.html” as follows.
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script src="../../node_modules/systemjs/dist/system.js"></script>
<script>
SystemJS.import('./jQuerySample.js');
</script>
</head>
<body>
<div id="MyElement">Loading...</div>
</body>
</html>
As you can see, we are loading “jQuerySample.js” with the help of SystemJs.
Step 7 – Start the server
In the command prompt, simply write following command to start the web server.
(“http-server” is the Node Module we installed in one of the previous lab. If you haven’t done it then fire “http-server –g” command to install it)
Step 8 – Test
Open the browser and browse for “Test.html”. You will get following error.
Understand the problem
In our case SystemJs (Module Loader) is responsible for loading other files at runtime. It means SystemJs is unable to find “jQuery”.
There are two very strange things happening.
- “jQuery” is a folder inside “node_modules”. It means SystemJs is trying to load folder. Module Loaders are meant for loading JS file dynamically and in our case it is trying to load folder.
- Secondly, “jQuery” folder is present inside the “node_modules” folder but SystemJs is searching for it inside root folder. (Examine the error carefully, it says, “Fetch error: 404 Not Found Instantiating http://192.168.0.100:8080/jquery”).
Let’s take a look at the generated “jQuerySample.js” file.
"use strict";
exports.__esModule = true;
var $ = require("jquery");
$(document).ready(function () {
$('#MyElement').html("<b>Hello World</b>");
});
If you remember we have written following statement in our TypeScript file.
import * as $ from "jquery"
Hence generate JS file contain following statement.
var $ = require("jquery")
It is supposed to be following.
var $= require("/node_modules/jquery/src/jquery.js")
Solution for the problem
Solution is “teaching”. You have to teach SystemJs that, loading “jquery” means loading /node_modules/jquery/src/jquery.js ". It can be done by creating SystemJs Configuration file.
Step 9 – Configure SystemJs
Create a new JS and name it “system.config.js”.
Now put following content inside it.
(function (global) {
System.config({
map: {
'jquery': '/node_modules/jquery/src/jquery.js'
},
});
})(this);
Include above file in “Test.html” after system.src.js and before main.js as follows.
<script src="../../node_modules/systemjs/dist/system.js"></script>
<script src="system.config.js"></script>
<script>
SystemJS.import('./jQuerySample.js');
</script>
Step 9 – Recheck the output
Switch to the browser and refresh the Page.
Note: When working with JavaScript you may get confused sometimes because of caching. Make sure to clear cache if the output is not as per the expectation.
Understand the problem
Take a while and try to see if you can think about reason for this error.
We have imported jQuery in “jQuerySample”. In the same way inside “jQuery” many other jQuery related files are imported, “selector.js” is one of them.
If you open “/node_modules/jquery/src” folder, you will notice “selector.js” over there.
That means this time path is correct. Then what’s the problem? Problem is file name.
It is looking for a file called “selector” which we don’t have. We have “selector.js” inside “/node_modules/jquery/src” folder.
Solution for this problem
Solution is again teaching. We have to teach SystemJs to add default extension to all the requests.
Step 10 – Reconfigure SystemJs
Add some more settings to “system.config.js” as follows.
(function (global) {
System.config({
map: {
'jquery': '/node_modules/jquery/src/jquery.js'
},
packages: {
'/': {
defaultExtension: 'js'
}
}
});
})(this);
Check the new “packages” section above.
It contains an entry which teaches SystemJs to add “js” extension to all requests made for files inside root (“/”) folder.
Step 11 – Recheck the output
Switch to the browser and refresh the Page.
Summary
Here is the end of part 2 and thus end of TypeScript learning.
It’s time to celebrate. You successfully completed all demos of day 1.
Now finally it’s time for Angular. See you on Day 2.
Stay tuned.
Stay tuned.
I am in twitter, Facebook, linked in.
Want to know more about me. Visit here
www.sukesh-marla.com
www.justcompile.com