chart

Introducing TypeScript

Large-scale JavaScript application development.

Remo H. Jansen

www.remojansen.com - @RemoHJansen - blog.wolksoftware.com

Why we need
TypeScript?

"Controlling complexity
is the essence of
computer programming"

— Brian Kernighan

What we know about software complexity?

Complex !== Complicated


- Complicated implies being difficult to understand but with time and effort, ultimately knowable.


- Complex, describes the interactions between a number of entities. If the number of entities and interactions between them increase we will get to a point where it would be impossible to know and understand all of them.

chart

“As a system evolves, its complexity increases unless steps are taken to reduce it.”

- "The eight laws of software evolution" by Meir Lehman


How can we measure the complexity of software?


Chidamber & Kemerer
object-oriented metrics suite

1. Number of methods defined in class (WMC)

2. Depth of inheritance tree (DIT)

3. Number of immediate sub-classes of a class (NOC)

4. Number of classes to which a class is coupled (CBO)



Note: The Chidamber & Kemerer object-oriented metrics suite is one of
the most common ways of measuring complexity of a system but there are
many other principles and techniques which can be applied for this purpose.

How can we reduce the complexity of a system?


The SOLID principles

Single responsibility principle

Open/Close principle

Liskov substitution principle

Interface segregation principle

Dependency inversion principle



Note: Adhering to the SOLID principles is one of the most common ways to reduce
the complexity of a system but there are many other principles and techniques which
can be applied for this purpose.

The principles, when applied together, intend to make it more likely that a programmer will create a system that is easy to maintain and extend over time


So why do we
need TypeScript?


We can write SOLID OOP code with JavaScript (ES5) but it feels so unnatural .

ES6 and ES7 make writing SOLID OOP code more natural but it will be years before we can use them without having to worry about old browsers.





Note: Some JavaScript developers don't like when I say this but I think that doing things like using closures to emulate a private property is not what I would consider as natural OOP (using the "private" access modifier).

TypeScript is the future of JavaScript

TypeScript allow us to use ES6 and ES7 today!

We can use the --target compiler option to specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES6'.


ES5 class

  1. var Person = (function () {
  2. function Person(name, surname) {
  3. this.name = name;
  4. this.surname = surname;
  5. }
  6. Person.prototype.greet = function () {
  7. return "Hello! my name is " + this.name + " " + this.surname;
  8. };
  9. return Person;
  10. })();

ES6 class

  1. class Person {
  2. public name;
  3. public surname;
  4.  
  5. constructor(name, surname) {
  6. this.name = name;
  7. this.surname = surname;
  8. }
  9.  
  10. public greet() {
  11. return `Hello! my name is ${this.name} ${this.surname}`;
  12. }
  13. }

ES5 inheritance

  1. var __extends = (this && this.__extends) || function (d, b) {
  2. for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
  3. function __() { this.constructor = d; }
  4. __.prototype = b.prototype;
  5. d.prototype = new __();
  6. };
  7.  
  8. var Animal = (function () {
  9. function Animal(theName) {
  10. this.name = theName;
  11. }
  12. Animal.prototype.move = function (meters) { /* ... */ };
  13. return Animal;
  14. })();
  15.  
  16. var Snake = (function (_super) {
  17. __extends(Snake, _super);
  18. function Snake(name) {
  19. _super.call(this, name);
  20. }
  21. Snake.prototype.move = function (meters) {
  22. /* ... */
  23. _super.prototype.move.call(this, meters);
  24. };
  25. return Snake;
  26. })(Animal);

ES6 inheritance

  1. class Animal {
  2. name:string;
  3. constructor(theName: string) { this.name = theName; }
  4. move(meters: number = 0) { /* ... */ }
  5. }
  6.  
  7. class Snake extends Animal {
  8. constructor(name: string) { super(name); }
  9. move(meters = 5) {
  10. /* ... */
  11. super.move(meters);
  12. }
  13. }

ES5 module (RequireJS)

  1. require([
  2. "./source/inversify.config",
  3. "./source/models/filter_state_model"
  4. ], function (kernel, FilterStateModel) {
  5.  
  6. // code goes here ...
  7.  
  8. });

ES6 modules

  1. import { kernel } from "./source/inversify.config";
  2. import { FilterStateModel } from "./source/inversify.config";
  3.  
  4. // code goes here ...

We can use the --module compiler option to specify module code generation: 'commonjs', 'amd', 'system', or 'umd'.

Callbacks (ES5)

  1. Parse.User.logIn("user", "pass", {
  2. success: function(user) {
  3. query.find({
  4. success: function(results) {
  5. results[0].save({ key: value }, {
  6. success: function(result) {
  7. // the object was saved.
  8. }
  9. });
  10. }
  11. });
  12. }
  13. });

Promises (ES6)

  1. Parse.User.logIn("user", "pass").then(function(user) {
  2. return query.find();
  3. }).then(function(results) {
  4. return results[0].save({ key: value });
  5. }).then(function(result) {
  6. // the object was saved.
  7. });
  1. Promise.all([Primise1, Primise2, PrimiseN]).then(function() {
  2. console.log("all the files were created");
  3. });

Async Functions (ES7)

  1. async function updateEntity() {
  2. var success = false;
  3. var user = await Parse.User.logIn(user, pass);
  4. if(user !== null) {
  5. let results = await query.find();
  6. success = await results[0].save({ key: value };
  7. }
  8. return success;
  9. }
  10.  
  11. await updateEntity();
  12. if(success) {
  13. // the object was saved.
  14. }

Decorators (ES6)

  1. @Component({
  2. selector: 'todo-app',
  3. appInjector: [
  4. AngularFire,
  5. bind(Firebase).toValue(new Firebase('https://webapi.firebaseio-demo.com/test'))
  6. ]})
  7. @View({
  8. templateUrl: 'todo.html',
  9. directives: [NgFor]
  10. })
  11. class TodoApp {
  12. todoService: FirebaseArray;
  13. todoEdit: any;
  14. todoFilter: Boolean;
  15.  
  16. constructor(sync: AngularFire) {
  17. this.todoService = sync.asArray();
  18. this.todoEdit = null;
  19. this.todoFilter = null;
  20. }
  21.  
  22. editTodo($event, todo) {
  23. this.todoEdit = todo;
  24. }
  25.  
  26. }

TypeScript goes beyond
ES6 & ES7

TypeScript is not a dynamic programming language
and introduces some great features that are not available
in ES6 or ES7!


Type inference

  1. function f() {
  2. return "hello";
  3. }

Code editors can incorporate the TypeScript language service.

logo

Optional type annotations

  1. function f(s: string) { return s; }
  2. // It is also possible to define the type of a function using the optional type annotations
  3. var f : (s: string) => string;
  4. f = function(s: string) { return s; }

Interfaces

  1. interface ISerializable { serialize() : string; }
  2. interface IMainSkill { use() : bool; }
  3.  
  4. interface IPerson {
  5. name : string;
  6. surname : string;
  7. greet() : string;
  8. }
  9.  
  10. class MockMainSkill implements IMainSkill {
  11. use() { return true; }
  12. }
  13.  
  14. describe("Person Class", () => { // TDD & BDD
  15. it("should be able to greet", () => {
  16. var mainSkill = new MockMainSkill(); // SOLID: Liskov substitution principle
  17. var luke : IPerson = new Person("Luke", "Skywalker", mainSkill);
  18. expect(luke.name).to.equal("Luke");
  19. expect(luke.surname).to.equal("Skywalker");
  20. expect(luke.greet()).to.equal("Hello! my name is Luke Skywalker");
  21. });
  22. }
  23.  
  24. class Person implements IPerson, ISerializable { // SOLID: Interface segregation principle
  25. private _skill : IMainSkill; // SOLID: Dependency inversion principle
  26. public name : string;
  27. public surname : string;
  28. constructor(name: string, surname: string, skill : IMainSkill) { /* ... */ }
  29. greet() : string { /* ... */ };
  30. serialize() : string { /* ... */ };
  31. }

Generics Types

  1. interface IValidatable {
  2. validate() : void;
  3. }
  4.  
  5. class CustomCollection<T extends IValidatable> { // SOLID: Open/Close principle
  6. private itemArray: Array<T>;
  7.  
  8. constructor() {
  9. this.itemArray = [];
  10. }
  11.  
  12. Add(item: T) {
  13. this.validate();
  14. this.itemArray.push(item);
  15. }
  16. // ...
  17. }
  18.  
  19. class User implements IValidatable{
  20. public name;
  21. validate() { if(name === null) throw new Error(); }
  22. }
  23.  
  24. class Message implements IValidatable{
  25. public message;
  26. validate() { if(message === null) throw new Error(); }
  27. }
  28.  
  29. var myMessage = new CustomCollection<Message>();
  30. var myUsers = new CustomCollection<User>();
  31. myUsers.Add(new User());
  32. myUsers.Add(new Message()); // Error because of generic type validation

Ok... you have convinced me but I have already started my project with Vanilla JavaScript...


Good news! TypeScript is a superset of JavaScript

All your JavaScript code is already valid TypeScript code.


Want to learn more?


Pre-order now! or join our next meetup for a chance
to win a free physical copy of my upcoming book.


Thanks!

Do you have any questions?

Remo H. Jansen

www.remojansen.com - @RemoHJansen - blog.wolksoftware.com