Generics and generic constraints

3.10 Generics and generic constraints

Wikipedia describes generic types as follows: In a generic type, a type parameters (T) is a placeholder for a specific type that a client specifies when they instantiate a variable of the generic type.

Getting Ready

All you need to be able to use generics is an installation of TypeScript 2.0 or higher.

How to do it…

We are going to start by declaring an interface and a type. We are going to declare this type just to be able to showcase a real life example of generics without having to go into too much detail.

interface IUtils { postAsync<T>(url: string, entity: T): boolean; }
declare var utils: IUtils;

Now that we have the type declarations ready we can proceed to implement a generic class named Service.

class Service<T> {
	private _url: string;
	
	constructor(url: string) {
		this._url = url;
	}
	
	save(entity: T): boolean {
     return utils.postAsync<T>(this._url, entity);
	}

   //...
}

How it works…

Sometimes we will encounter cases in which we will find our selves writing some classes around our entities. If we are working on the implementation of things like a database repository or a web service client, we will probably write almost identical pieces of code. Sometimes, the only difference between one repository class or a web service client and another would be the types of the entities. In those scenarios we can replace the types with a generic type (T) and specify it when creating the instances of the generic class:

class Client { email: string; }
const clientService = new Service<Client>('/api/client');
clientService.save(new Client());

class Order { orderId: number; }
const orderService = new Service<Order>('/api/order');
orderService.save(new Order());

There’s more…

Sometimes you will need to invoke a method or access a property of the generic type T which is not part of the Object prototype. In that case, you will need to use a generic type constraint to limit the types that can be used as the generic type (T). You should start by declaring an interface:

interface IValidatable {
	isValid(): boolean;
}

And continue by the creation of a generic constraint:

class Service<T extends IValidatable> {
	private _url: string;
	
	constructor(url: string) {
	  this._url = url;
	}
	
	save(entity: T): boolean {
	  if(entity.isValid()) {
	    return utils.postAsync<T>(this._url, entity);
	  }
	}
}

Once we have declared the generic class constraint we will need to ensure that all the entities that will be used as the generic type (T) satisfy that constraint.

class Client implements IValidatable {
	email: string;
	isValid() { return true };
}
const clientService = new Service<Client>('/api/client');
clientService.save(new Client());

class Order implements IValidatable {
	orderId: number;
	isValid() { return true };
}
const orderService = new Service<Order>('/api/order');
orderService.save(new Order());

Source Code

Generics and generic constraints

Class Expressions vs. Class Declarations

3.9 Class Expressions vs. Class Declarations

ES6 supports two styles of creating classes: class declarations and class expressions. The TypeScript the 1.6 release introduces support for class expressions.

Getting Ready

All you need to be able to use class expressions is an installation of TypeScript 2.0 or higher.

How to do it…

The following code snippet showcases how to use a class expression:

const Recto = class {
  area: number;

  constructor(public length: number, public width: number) {
    this.area = this.length * this.width;
  }
}

The equivalent using a class declaration would be:

class Rect {
  area: number;

  constructor(public length: number, public width: number) {
    this.area = this.length * this.width;
  }
}

How it works…

As we can observe in the two preceding code snippets, the main differences between the two styles are:

  • Class declarations can be unnamed.
  • Class declarations require the definition of a variable to store a reference to the declared class when the class is unnamed.

By observing the generated JavaScript code for the class expression:

var Rect = (function () {
    function class_1(length, width) {
        this.length = length;
        this.width = width;
        this.area = this.length * this.width;
    }
    return class_1;
})();

And the class declaration:

var Rect = (function () {
    function Rect(length, width) {
        this.length = length;
        this.width = width;
        this.area = this.length * this.width;
    }
    return Rect;
})();

We can also notice that class expressions use an internal name (class_1), which is not available outside the scope of the class declaration.

Source Code

Class Expressions vs. Class Declarations

Intersection types

3.8 Intersection types

Anders Hejlsberg, the creator of TypeScript, describes intersection types as:

Intersection types are the logical complement of union types. A union type A | B represents an entity that has either type A or type B, whereas an intersection type A & B represents an entity that has both type A and type B.

Getting Ready

All you need to be able to use intersection types is an installation of TypeScript 2.0 or higher.

How to do it…

Let’s start by declaring a few types:

interface A { a: string }
interface B { b: string }
interface C { c: string }

Now, we can use the intersection type operator (&) to create a new intersection type:

let abc: A & B & C;

How it works…

The intersection type abc includes all the method and properties declared by the A, B and C interfaces:

abc.a = 'hello';
abc.b = 'hello';
abc.c = 'hello';

There’s more…

When a member is available in two or more of the types used to create an intersection type under the same name but different type:

interface X { x: A }
interface Y { x: B }
interface Z { x: C }
let xyz: X & Y & Z;

The member in the resulting intersection type will be available under the same name but will contain all the properties from the types involved in the intersection:

xyz.x.a = 'hello';
xyz.x.b = 'hello';
xyz.x.c = 'hello';

This behavior is similar to the usage of the following mixins:

  applyMixins(xyz.x, [xyz.x, xyz.x]);

Source Code

Intersection types

See also

Please refer to the recipes about <div class="important-text">union types</div> & <div class="important-text">mixins</div> in this chapter to learn more about them.

Mixins

3.7 Mixins

Mixins is an alternative to inheritance and composition. We can use mixins to create classes combining simpler partial classes.

Getting Ready

All you need to be able to use mixins is an installation of TypeScript.

How to do it…

To create mixins we need to declare the following function somewhere in our code base:

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        });
    });
}

How it works…

Let’s declare to simple classes:

class Climbs {
  climb() {
    alert('My spider-sense is tingling.');
  }
}

class Bulletproof {
  deflect() {
    alert('My wings are a shield of steel.');
  }
}

We can then create classes which include the behavior of the two preceding classes:

class BeetleGuy implements Climbs, Bulletproof {
  climb: () => void;
   deflect: () => void;
}

We can use the applyMixins class to copy the implementation of the two declared methods:

applyMixins(BeetleGuy, [Climbs, Bulletproof]);

Source Code

Mixins

Abstract classes

3.6 Abstract classes

An abstract class is a class that cannot be instantiated, but allow the creation of sub-classes.

Getting Ready

All you need to be able to use abstract classes is an installation of TypeScript version 2.0 or higher.

How to do it…

We can declare an abstract class using the abstract keyword. Note that it is also possible to apply the abstract keyword to some of the methods of the abstract class:

abstract class Base {
  foo(): number { return this.bar(); }
  abstract bar(): number;
}

How it works…

Abstract class cannot be instantiated so if we try to create an instance of the preceding class the TypeScript compiler will throw compilation an error:

const b = new Base;
However, we are allowed to create derived classes from the Base class:
class Derived extends Base {
	bar(): number {
	  return 3;
	}
}

We can then create an instance of the derived class:

const derived = new Derived; // OK
derived.bar(); // 3

The derived class will inherit the non-abstract methods from its base class:

derived.foo(); // 3

Source Code

Abstract classes

Local types

3.5 Local types

Local types allow us to declare a type inside the declaration of another type.

Getting Ready

All you need to be able to use local types is an installation of TypeScript version 2.0 or higher.

How to do it…

TypeScript 1.6 onwards allow us to use the keywords related with the declaration of types (class, interface, type…) inside the declaration of a type:

class A {
	x: number;
	do() {
	  class B {
	    y: string;
	  }
	  return new B();
	}
}

How it works…

The TypeScript classes are just translated into JavaScript classes keeping the original position within the parent type. For example, the B class is declared inside the do method, which belongs to the A class.

var A = (function () {
    function A() {
    }
    A.prototype.do = function () {
        var B = (function () {
            function B() {
            }
            return B;
        })();
        return new B();
    };
    return A;
})();

Source Code

Local types

Access modifiers

3.4 Access modifiers

TypeScript allows us to use some of the reserved keywords private, public and protected to control the access to class methods and properties.

  • Public members are directly accessible by both the base and derived class.
  • Private members are only accessible within the class defining them.
  • Protected members are accessible in the class that defines them and in classes that inherit from that class.

Getting Ready

All you need to be able to use access modifiers is an installation of TypeScript 2.0 or higher.

How to do it…

Let’s declare a class named Base with public, private and protected properties and methods:

class Base {
	protected msg = 'Protected!';

	private _doPrivate() {
		console.log('Private! ');
	}

	public doPublic() {
		console.log('Public!');
	}

	public accessPrivate(){
		this._doPrivate();
	}
}

How it works…

Now, let’s declare a derived class and create an instance to see how the access modifiers behave:

class Derived extends Base {
	public accessProtected() {
		this.msg;
	}
}
 const derived = new Derived();

If we try to invoke the class methods and properties, we will be able to observe the access modifiers rules in action:

derived.doPublic();        // OK
derived.accessPrivate();   // OK
derived.accessProtected(); // OK
derived.msg;               // Error
derived._doPrivate();      // Error

Source Code

Access modifiers

Arrow functions and the polymorphic this operator

3.3 Arrow functions and the polymorphic this operator

Arrow functions expressions allow us to declare functions in a more compact way and have been designed to work particularly well when working with callbacks.

Getting Ready

All you need to be able to use arrow functions and the polymorphic this operator is an installation of TypeScript version 2.0 or higher.

How to do it…

It is common to encounter issues caused because the this operator is pointing to the wrong object when working with event handlers and callbacks:

class Form {
	
	constructor(){
		this.initializeEvents();
	}
	
	initializeEvents() {
		$('#submitBtn').on('click', function() {
			this.onSubmit(); // Error
		});
	}
	
	onSubmit() {
		// ...
	}
}

We can solve this issue by using an arrow function:

class Form {

	constructor(){
		this.initializeEvents();
	}
	
	initializeEvents() {
		$('#submitBtn').on('click', () => {
			this.onSubmit(); // OK
		});
	}
	
	onSubmit() {
		// ...
	}
}

How it works…

The this operator points to a class when it is accessed from one of the methods of the class but if we define a new function inside of one of its methods the this operator starts to point to the new function when inside it. Inside the event handler for the click event the this operator is not pointing to the Form class; it is pointing to the event handler function. This explains why the onSubmit method can not be found. In the second example we used an arrow function to declare the event handler. If we take a look to the generated JavaScript output, we will be able to see that the TypeScript compiler used a variable named _this to have reference to the Form class from the event handler:

Form.prototype.initializeEvents = function () {
  var _this = this;
  $('#submitBtn').on('click', function () {
    _this.onSubmit();
  });
};

There’s more…

TypeScript 1.7 introduces a new feature known as polymorphic this operator which allow us to use the this operator as the return type of the methods of a class:

class Binding {
	private _scope: string;
	
	inSingletonScope(): this {
	  this._scope = 'singleton';
	  return this;
	}
	
	inTransientScope(): this {
	  this._scope = "transient";
	  return this;
	}
	
	onActivation(cb: () => void) {
	  cb();
	}
}

This new feature makes it easier to write fluent interfaces:

const binding = new Binding().inSingletonScope().onActivation(() => {
    console.log('activating...');
  });

Source Code

Arrow functions and the polymorphic this operator

See also

Please refer to the Mozilla developer network to learn more about all the rules that are involved in the behavior of the this operator at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

Closures

3.2 Closures

The Mozilla development network defines closures as follows:

Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created.

Getting Ready

All you need to be able to use closures is an installation of TypeScript 2.0 or higher.

How to do it…

We are going to implement an object which will allow us to increment and decrement the value of a numeric counter. However, the counter itself will remain private thanks to the usage of closures:

const counter = (function () {
    let i = 0;

    const counter = {
      next: function () {
        i = i + 1;
        return i;
      },
      prev: function () {
        i = i - 1;
        return i;
      }
    };

    return counter;
  })();

If we invoke the methods next and prev, we will be able to observe how the value of the counter changes:

counter.next(); // 1
counter.next(); // 2
counter.next(); // 3
counter.prev(); // 2
counter.prev(); // 1

How it works…

The preceding examples uses two functions (prev and next) defined inside a closure (the IIFE function). The two functions refer to independent free variables, or, in other words, remember the environment in which they were created. In this case, the only free variable is the counter named i, this explains how is it possible that the counter is not cleared from memory after the closure (the IIFE function) has been executed.

Closures can be used to emulate static and private variables at runtime but they can lead us to memory that will never be freed, so make sure that you are careful when you use them.

Source Code

Closures

See also

Please refer to the recipe about Immediately-Invoked Function Expressions to learn about more practical usages of closures.

IIFE immediately invoked function expressions

After learning how to configure your development environment and how to automatize some of the tasks in your development workflow with Gulp, we will continue our journey towards our goal: becoming a full-stack TypeScript engineer. In this chapter, we will learn some effective usages of some of the core building blocks of the TypeScript programming language: functions and classes. You will learn about the following topics:

  • IIFE immediately invoked function expressions
  • Closures
  • Arrow functions and the polymorphic this operator
  • Access modifiers
  • Local types
  • Abstract classes
  • Mixins
  • Intersection types
  • Class Expressions vs. class Declarations
  • Generics and generic constraints
  • Tag functions and tagged templates
  • Destructuring

3.1 IIFE immediately invoked function expressions

Immediately-Invoked Function Expressions (IIFE) are functions expressions that are instantly invoked after their declaration.

Getting Ready

All you need to use IIFE is an installation of TypeScript 2.0 or higher.

How to do it…

IIFE (also known as self-executing anonymous function) are just like any other function but they are wrapped with parenthesis and invoked immediately after:

(function() {
  // the code here is executed once in its own scope
})();

How it works…

If you warp a variable with parenthesis in a TypeScript or JavaScript statement, the result of executing the statement will be the variable which was wrapped by the parenthesis:

console.log(1, (1)); 
console.log('test', ('test')); 
console.log(true, (true)); 
console.log(new Date().getTime(), (new Date().getTime())); 

The lines of code above log the following in console:

1 1
test test
true true
1448287535398 1448287535398

We can use this little trick to invoke a function after it has been declared:

 (function() { console.log('test'); })

Once the function has been wrapped, we can we can invoke it:

 (function() { console.log('test'); })();

There’s more…

Immediately-invoked function expressions can be used to:

  • Avoid variable hoisting from within blocks
  • Protect against polluting the global
  • Emulate private and static variables.

    Note that it is possible to pass arguments to an IIFE: ts(function(global) { console.log('test'); })(window);

Source Code

IIFE immediately invoked function expressions

See also

Please refer to the recipes about closures and scope in this chapter to learn more about possible applications of Immediately-Invoked Function Expressions (IIFE).