Book Image

TypeScript 2.x for Angular Developers

By : Christian Nwamba
Book Image

TypeScript 2.x for Angular Developers

By: Christian Nwamba

Overview of this book

<p>TypeScript, a superset of JavaScript, is the default language for building Angular applications from Google. TypeScript 2.0 adds optional static types, classes, and modules to JavaScript, to enable great tooling and better structuring of large JavaScript applications.</p> <p>This book will teach you how to leverage the exciting features of TypeScript while working on Angular projects to build scalable, data-intensive web applications. You will begin by setting up and installing TypeScript and then compile it with JavaScript. You will then learn to write simple JavaScript expressions with TypeScript. You will then go through some built in TypeScript types and accessors. Further you will build web components using TypeScript Interfaces and Decorators. You will go through the Angular form module, which will enable you to write predictable typed forms with TypeScript and learn to use typed DOM events. You will also build a single-page application using the Router module. Lastly, you will consume API data with Angular's HTTP Module and handle responses with RxJS observables. At the end of the book, you will go through different testing and debugging techniques.</p>
Table of Contents (20 chapters)
Title Page
Credits
About the Author
Acknowledgments
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface
9
Writing Modules, Directives, and Pipes with TypeScript

Implications of loose types


Let's start out with an example to show how loosely typed languages behave:

// Code 1.1

// Declare a variable and assign a value
var x = "Hello";

// Down the line
// you might have forgotten 
// about the original value of x
//
//
// Re-assign the value
x = 1;

// Log value
console.log(x); // 1

The variable x was initially declared and assigned a string value, Hello. The same x  got re-assigned to a numeric value, 1. Nothing went wrong; the code was interpreted and when we logged the value to the console, it logged the latest value of x, which is 1.

This is not just a string-number thing; the same thing applies to every other type, including complex data structures:

// Code 1.2

var isCompleted;

// Assign null
isCompleted = null;
console.log('When null:', isCompleted);

// Re-assign a boolean
isCompleted = false;
console.log('When boolean:', isCompleted);

// Re-assign a string
isCompleted = 'Not Yet!';
console.log('When string:', isCompleted);

// Re-assign a number
isCompleted = 0;
console.log('When number:', isCompleted);

// Re-assign an array
isCompleted = [false, true, 0];
console.log('When array:', isCompleted);

// Re-assign an object
isCompleted = {status: true, done: "no"};
console.log('When object:', isCompleted);

/**
* CONSOLE:
*
* When null: null
* When boolean: false
* When string: Not Yet!
* When number: 0
* When array: [ false, true, 0 ]
* When object: { status: true, done: 'no' }
*/

The important thing to note here is not that the values are changing. Rather, it's the fact that both values and types are changing. The change in the type does not affect the execution. Everything works fine, and we have our expected result in the console.

The function parameters and return types are not left out either. You can have a function signature that accepts a string parameter, but JavaScript will keep silent when you, or any other developer, pass in a number while calling the function:

function greetUser( username ) {
 return `Hi, ${username}`
}

console.log('Greet a user string: ', greetUser('Codebeast'))
console.log('Greet a boolean: ', greetUser(true))
console.log('Greet a number: ', greetUser(1))

/**
 * CONSOLE:
 *
 * Greet a user string: Hi, Codebeast
 * Greet a boolean: Hi, true
 * Greet a number: Hi, 1
 */

If you're coming from a strong-type background and have no previous experience with loosely typed languages, the preceding example must feel weird. This is because in strongly typed languages, it's hard to change the type of the particular member (variables, functions, and so on).

So, what is the implication to take note of? The obvious implication is that the members that are loosely typed are inconsistent. Therefore, their value types can change, and this is something that you, the developer, will need to watch out for. There are challenges in doing so; let's talk about them.

The problem

Loose types are tricky. At first glance, they appear to be all nice and flexible to work with--flexibility, as in giving you the freedom to change types anytime and anywhere, without the interpreter screaming errors like other strongly typed languages do. Just like any other form of freedom, this one also comes with a price.

The major problem is inconsistency. It is very easy to forget the original type for a member. This could lead you to handling, say, a string as if it were still a string when its value is now Boolean. Let's see an example:

function greetUser( username ) {
 // Reverse the username
 var reversed = username.split('').reverse().join('');
 return `Hi, ${reversed}`
}

console.log('Greet a correct user: ', greetUser('Codebeast'))


 * CONSOLE:
 *
 * Greet a correct user: Hi, tsaebedoC
 */

In the preceding example, we have a function that greets the users based on their usernames. Before it does the greeting, it first reverses the username. We can call the function by passing in a username string.

What happens when we pass in a Boolean or some other type that does not have a split method? Let's check it out:

// Code 1.4

function greetUser( username ) {
 var reversed = username.split('').reverse().join('');
 return `Hi, ${reversed}`
}

console.log('Greet a correct user: ', greetUser('Codebeast'))

// Pass in a value that doesn't support
// the split method
console.log('Greet a boolean: ',greetUser(true))


 * CONSOLE:
 *
 * Greet a correct user: Hi, tsaebedoC
 * /$Path/Examples/chapter1/1.4.js:2
 * var reversed = username.split('').reverse().join('');
                          ^
 * TypeError: username.split is not a function
 */

The first log output, which prints the greeting with a string, comes out fine. But the second attempt fails because we passed in a Boolean. In as much as everything in JavaScript is an object, a Boolean does not have a split method. The image ahead shows a clear output of the preceding example:

Yes, you might be thinking that you're the author of this code; why would you pass in a Boolean when you designed the function to receive a string? Remember that a majority of the code that we write in our lifetime is not maintained by us, but by our colleagues.

When another developer picks up greetUser and decides to use the function as an API without digging the code's source or documentation, there is a high possibility that he/she won't pass in the right value type. This is because he/she is blind. Nothing tells him/her what is right and what is not. Even the name of the function is not obvious enough to make her pass in a string.

JavaScript evolved. This evolution was not just experienced internally but was also seen in its vast community. The community came up with best practices on tackling the challenges of the loose-type nature of JavaScript.

Mitigating loose-type problems

JavaScript does not have any native obvious solution to the problems that loose types bring to the table. Rather, we can use all forms of manual checks using JavaScript's conditions to see whether the value in question is still of the intended type.

We are going to have a look at some examples where manual checks are applied in order to retain the integrity of the value types.

The popular saying that Everything is an Object in JavaScript is not entirely true (https://blog.simpleblend.net/is-everything-in-javascript-an-object/). There are Objects and there are Primitives. Strings, numbers, Boolean, null, undefined, are primitives but are handled as objects only during computation. That's why you can call something like .trim() on a string. Objects, arrays, dates, and regular expressions are valid objects. It's mind-troubling to say that an object is an object, but that is JavaScript for you.

The typeof operator

The typeof operator is used to check the type of a given operand. You can use the operator to control the harm of loose types. Let's see some examples:

// Code 1.5
function greetUser( username ) {
 if(typeof username !== 'string') {
   throw new Error('Invalid type passed');
 };
 var reversed = username.split('').reverse().join('');
 return `Hi, ${reversed}`
}

console.log('Greet a correct user: ', greetUser('Codebeast'))
console.log('Greet a boolean: ',greetUser(true))

Rather than waiting for the system to tell us that we're wrong when an invalid type is passed in, we catch the error as early as possible and throw a custom and more friendly error, as shown in the following screenshot:

The typeof operator returns a string, which represents the value's type. The typeof operator is not entirely perfect and should only be used when you are sure about how it works. See the following issue:

function greetUser( user ) {
 if ( typeof user !== 'object' ) {
   throw new Error('Type is not an object');
 }
 return `Hi, ${user.name}`;
}

console.log('Greet a correct user: ', greetUser( {name: 'Codebeast', age: 24 } ))
// Greet a correct user: Hi, Codebeast

console.log('Greet a boolean: ', greetUser( [1, 2, 3] ))
// Greet a boolean: Hi, undefined

You may have expected an error to be thrown when the function was called with an array for the second time. Instead, the program got past the check and executed user.name before realizing that it is undefined. Why did it get past this check? Remember that an array is an object. Therefore, we need something more specific to catch the check. Date and regex could have passed the check as well, even though that may not have been the intent.

The toString method

The toString method is prototypically inherited by all the objects and wrapped objects (primitives). When you call this method on them, it returns a string token of the type. See the following examples:

Object.prototype.toString.call([]);// [object Array]Object.prototype.toString.call({});// [object Object]Object.prototype.toString.call('');// [object String]Object.prototype.toString.call(newDate());// [object Date]
// etc

Now you can use this to check the types, as shown by Todd Motto (https://toddmotto.com/understanding-javascript-types-and-reliable-type-checking/#true-object-types):

var getType = function (elem) {
 return Object.prototype.toString.call(elem).slice(8, -1);
};
var isObject = function (elem) {
 return getType(elem) === 'Object';
};

// You can use the function
// to check types
if (isObject(person)) {
 person.getName();
}

What the preceding example does is check the part of the string returned by the toString method to determine its type.