TypeScript is a superset of JavaScript and over the last year it’s gaining popularity by leaps and bounds. Angular 2 and RxJS 5 are written in Typescript. I believe about a million of developers are using TypeScript today for app development (this is not official stats). I’m using TypeScript for more than a year and it’s so much more productive than JavaScript! For me (a Java developer), TypeScript makes a lot more sense than JavaScript. But if your main language was JavaScript, some of the TypeScript’s concepts might look foreign for you. I’m planning to write a couple of blogs illustrating TypeScript syntax.
Web browsers don’t understand TypeScript and there’re no plans to change this any time soon. So if you’ll write a program in TypeScript, it has to by transpiled (think compiled) into JavaScript first. I’m not going to discuss the TypeScript compiler here, but will be using the Playground, where you can write code fragments in TypeScript, and they’ll be immediately transpiled into JavaScript (it’s going to be the ECMAScript 5 version).
TypeScript supports different flavors of interfaces. Today we’ll get familiar with a callable interface that contains a bare function signature (a signature without a function name). I’ll show you the syntax first and then will explain how a callable interfaces are useful. The following example shows a bare function signature that takes one parameter of type number and returns a boolean.
(percent: number): boolean;
The bare function signature indicates that when this function will be implemented it’s going to be callable. So what’s the big deal? Let’s consider an example that declares IPayable interface, which will contain a bare function signature. In our company work employees and contractors that will be represented by a class Person. The rules for increasing pay of employees and contractors are different, and I’ll create separate functions that implement these rules. These functions will be passed as arguments to the constructor of the class Person and will be invoked inside the constructor of the Person instances.
interface IPayable { // <1> (percent: number): boolean; } class Person { constructor(private validator: IPayable) { // <2> } increasePay(percent: number): boolean { // <3> return this.validator(percent); } } let forEmployees: IPayable = (percent) => { // <4> console.log("Increasing salary by ", percent); return true; }; let forContractors: IPayable = (percent) => { // <5> var increaseCap: number = 20; if (percent < increaseCap) { console.log("Increasing hourly rate by", percent); return true; } else { console.log("Sorry, the increase cap for contractors is ", increaseCap); return false; } } let workers: Array<Person> = []; workers[0] = new Person(forEmployees); // <6> workers[1] = new Person(forContractors); workers.forEach(worker =>worker.increasePay(30)); // <7>
1. A callable interface that include a bare function signature.
2. We declare that the constructor of the Person class takes an implementation of the callable interface IPayable as an argument.
3. The increasePay() method invokes the bare function on the passed implementation of IPayable, supplying the pay increase value for validation.
4. The rules for salary increases for employees are implemented using the arrow function expression.
5. The rules for pay increases for contractors are implemented using the arrow function expression.
6. Instantiates two Person objects, passing different rules for pay increases.
7. Invokes increasePay() on each instance, validating the 30% pay increase.
First I’ll enter the above code in the TypeScript playground on the left, and the generated JavaScript code will be shown on the right (see http://bit.ly/2hUdsGn). Read the JavaScript code – it should help you understanding what’s going on. Note, that there are no traces of our IPayable interface on the right since JavaScript doesn’t support interfaces.
If you click on the button Run and open the browser’s console you’ll see the following output:
Increasing salary by 30 Sorry, the increase cap for contractors is 20
Cool. But in JavaScript you can also pass a function as an argument to a higher order function (constructor in our case), right?
Now imagine that you’re supposed to pass a function with a certain signature to a higher order function, but made a mistake and passed a wrong function. This will result in a runtime error.
Callable interfaces allow you to to catch this mistake during the development stage. For that we declare the signature of a function that has to be passed to the constructor of the instance of the Person object.
Now purposely introduce an error – declare a function with the wrong signature (do it on the left side at the playground):
let forTempWorkers = () => console.log("Can't increase salary for temps");
Try to pass it to the constructor to a Person:
workers[0] = new Person(forTempWorkers);
TypeScript will immediately highlight the above line as erroneous, and you’ll catch this error during dev time whereas in JavaScript this error would silently sneak into your code causing the app to blow up during the runtime.
So callable interfaces are your friends, aren’t they?