Hace poco un lector me preguntaba ¿Qué es mejor para trabajar con Ionic 2 (o Angular 2): ¿Aprendo ES6 o TypeScript? y sobretodo…¿Por qué debo elegir uno u otro?

La comunidad Angular prefiere TypeScript

Antes de nada decir que en Angular2 se puede trabajar con Javascript «clásico» (ES5), así como ES6 y TypeScript. No obstante Angular2 recomienda utilizar TypeScript y últimamente estamos viendo movimientos en frameworks que utilizan Angular2 (Ionic 2 por ejemplo), así como en developers relevantes dentro de la comunidad, que también abogan por el uso de TypeScript por encima de ES6.

Viendo esta situación, la primera pregunta que se te viene a la cabeza es ¿por qué? ¿qué tiene de especial o mejor TypeScript? ¿porqué debo aprender otro lenguaje en lugar del ESTÁNDAR?

Te recomiendo que leas todo el artículo para entender en detalle los pros y contras, pero si quieres, puedes ir directamente a las conclusiones.

¿Qué es TypeScript?

Para responder a la pregunta anterior, lo primero es entender qué es TypeScript.

TypeScript es un superset de ECMAScript 6, es decir, incluye todas las funcionalidades de ES6, y además incorpora una capa por encima con funcionalidades extra.

Esto, entre otras cosas, significa que tu puedes mezclar código TypeScript con código ES6 estándar sin problema, y el compilador de TypeScript seguirá pasando el código a ES5 (recordemos que tanto ES6 como TypeScript se transpilan a ES5 para que los navegadores actuales puedan ejecutar el código).

TypeScript es un superset de ECMAScript 6, por lo que puedes mezclar TypeScript con ES6 estándar en tu código.

De esto se deriva otra conclusión: La pregunta no debe ser ¿Aprendo ES6 o TypeScript?. Para usar TypeScript hay que saber ES6, por lo que en todo caso, la pregunta es: ¿Aprendo TypeScript?

ES6 deberías aprenderlo sí o sí. La pregunta que te debes hacer es ¿Aprendo también TypeScript?

TypeScript as a JS superset

Diferencias entre ES6 y TypeScript

Variables tipadas

La principal característica de TypeScript por encima de Javascript es que permite definir de qué tipo son las variables que se van a usar.

Veamos un ejemplo. En este caso, voy a crear una clase que tenga una propiedad de tipo array, pero introduciré algún error a propósito.

Versión Javascript:

class MyClass{
    constructor(someArg){
        this.myArray = someArg;
    }
    someMethod(){
        for(let item of this.myArray){
            console.log(item);
        }
    }
}
let someVar = 123456;
let myClassInstance = new MyClass(someVar);
myClassInstance.someMethod();

 
Versión TypeScript:

class MyClass{
  myArray:Array<number>;
    constructor(someArg:string){
        this.myArray = someArg;
    }
    someMethod(){
        for(let item of this.myArray){
            console.log(item);
        }
    }
}
let someVar:number = 123456;
let myClassInstance:MyClass = new MyClass(someVar);
myClassInstance.someMethod();

¿Qué error hay en el ejemplo? -> En ambos casos intento pasar un número a una propiedad que utilizaré más adelante como un array.

¿Resultado?

  • En ES6, no tengo ninguna pista del error, pero fallará en tiempo de ejecución al llamar a someMethod() por que intento utilizar un método de Iterators (el bucle for…of), y la propiedad es un número, que no dispone de dicho método.
  • En TypeScript, en cambio, recibo 2 errores en tiempo de compilación:
    • En primer lugar, me dirá que le estoy intentando asignar un string a un array (primer error que he introducido a propósito en el constructor)
    • En segundo lugar, me dice que no puedo pasar someVar al constructor de MyClass, por que NO es del tipo esperado.

Como vemos, utilizar TypeScript en este ejemplo, ha evitado que me encuentre un error en tiempo de ejecución. Si tengo el plugin adecuado, el mismo editor de código me señalará el error y lo podré arreglar al instante, sin necesidad de ejecutar el código.

TypeScript aporta por tanto mayor robustez, pero en contrapartida hemos tenido que escribir algo más y sobretodo, hemos perdido la flexibilidad de poder pasar lo que queramos en los argumentos.

El uso de variables tipadas de TypeScript aporta mayor robustez al código, pero por contra nos hace perder flexibilidad.

Datos miembro públicos/privados

Un detalle que a veces pasa desapercibido de TypeScript, es que en el constructor podemos declarar directamente los parámetros que recibimos como datos miembro públicos o privados. Es decir, no solo indico si es público o privado, sino que además automáticamente realizo la asignación del parámetro al dato miembro.

En la práctica esto significa que el siguiente código TypeScript:

class HelloWorld {
    constructor(private message: string) {
    }
}

Al compilarse a ES5 se transforma automáticamente en:

class HelloWorld {
    constructor(message: string) {
        this.message = message;
    }
}

Con el beneficio adicional de que si intento acceder al dato miembro message desde fuera de la clase HelloWorld, el compilador de TypeScript me lanzará un error por que lo he definido como privado.

Decorators

La otra característica principal de TypeScript sobre ES6 son los decoradores -y atención, por que estos decoradores están propuestos para ES7, así que probablemente en un futuro formen parte del estandar JS-.

Por si no lo conocías, el patrón de diseño decorador sirve para añadir funcionalidad a un objeto de forma dinámica. Veamos un pequeño ejemplo para entendernos:

const logger = (input) => console.log(input);

const finiteNumbers = (fn) =>
  (item) => {
    if(Number.isFinite(item))
      return fn(item);
  }

[1, "hola", -5, null].map(finiteNumbers(logger)); //1,-5

En el ejemplo, finiteNumbers decora cualquier función que le pasemos como argumento, haciendo que solo se ejecute cuando su valor de entrada sea un número finito.

Como acabamos de ver, nada te impide utilizar este patrón con Javascript tradicional o ES6. Sin embargo lo que acabo de mostrar es un ejemplo muy sencillo, y cuando intentamos aplicar decoradores a clases no estáticas su complejidad aumenta considerablemente.

En cambio, TypeScript proporciona una sintaxis especial para simplificar el uso del patrón decorador. Sin entrar en detalles de como implementarlos, es importante conocer como usarlos, ya que cobran protagonismo en Angular 2.

Los decoradores se pueden utilizar antes de declarar una clase, propiedad, método o parámetro, y utilizan la sintaxis @myDecorator(args)

Veamos un ejemplo con todos ellos:

@SomeClassDecorator(/*some parameters*/)
class MyDecoratedClass {

    @SomePropertyDecorator()
    myDecoratedProperty: string;

    @SomeMethodDecorator()
    myDecoratedMethod(@myParamDecorator() decoratedParam: number) {
    }
}

Entonces… ¿paso del estandar y uso TypeScript?

Tengo que reconocer que al principio opuse cierta resistencia a abandonar ES6 puro y utilizar TypeScript. Lo normal es que te enfrentes a la duda de ¿qué tiene TypeScript que lo haga especial para Angular2?.

Angular 2 utiliza el patrón decorador

Angular 2 utiliza el patrón decorador a lo largo de toda su arquitectura.

Vamos a ver las diferencias mediante un ejemplo. Crearemos un componente básico que recibirá un objeto user y mostrará su propiedad name. La llamada desde el template sería así:

<my-user [user]="selectedUser"></my-user>

Definamos este componente con TypeScript:

import { Component, Input } from '@angular/core';

@Component({
  selector: 'my-user',
  template: '<h1>User {{user.name}}</h1>'
})
export class UserComponent{
    @Input()
    user: User = null;
    constructor(){}
}

Veamos ahora como quedaría la misma definición en ES6:

import {Component} from 'angular@/core';

let componentAnnotation = new Component({
  selector: 'my-user',
  template: '<h1>User {{user.name}}</h1>',
  inputs: ['user']
});
export class UserComponent{
    constructor(){
        this.user = null;
    }
}

UserComponent.annotations = [componentAnnotation];

Podemos ver que en TypeScript usamos 2 decoradores: @Component y @Input. El código queda muy compacto y se entiende fácilmente.

OFERTA
Curso

Componentes Angular Nivel PRO

Domina los componentes de Angular (Angular 2/4/5+) como un experto y crea componentes técnicamente brillantes.
Idioma: Español
23 €110 €

En ES6 tenemos que crear el objeto componente, y añadirlo posteriormente a la clase UserComponent mediante su propiedad annotations. Además, en el componente tenemos que incluir un campo inputs donde pasarle un array de strings con las propiedades que queremos que sean de entrada. Más allá de que podemos caer en errores de escritura, el uso de ES6 puro nos ha llevado a escribir más código.

Desde mi punto de vista, el primer código es mucho más elegante, pero si te resistes a TypeScript, la buena noticia es que tienes un plugin de Babel que te permite utilizar estos decoradores con ES6 y sin necesidad de TypeScript.

Es más sencillo usar Angular 2 con la sintaxis de decorators de TypeScript. Pero también puedes utilizarlos con el plugin transform-decorators-legacy de Babel sin necesidad de recurrir a TypeScript.

Dependency Injection

Angular 2 mantiene el concepto de Inyección de Dependencias que traía AngularJS. Un factor que se criticaba bastante de Angular es que la DI requería de escribir varias veces lo mismo, con el riesgo de cometer algún error.

Veamos un ejemplo de como se soluciona la DI en Angular 2 con ES6:

import {UserService} from './user.service';

export class AnotherService {
  constructor(userService) {
    this.userService = userService;
  }
}
AnotherService.parameters = [[UserService]];

Como vemos, en Angular 2 pasamos los servicios que necesitamos al constructor y se los asignamos como dato miembro. No obstante, necesitamos indicar de algún modo la inyección de dependencias. Esto lo hacemos con la propiedad parameters de la clase.

¿Y qué pasa con TypeScript? Angular 2 se beneficia de los parámetros tipados para inferir la DI. Veamos el código:

import {UserService} from './user.service';

export class AnotherService {
  constructor(private userService:UserService) {}
}

Gracias a que podemos pasar el tipo del servicio en el constructor, Angular 2 detecta que la clase depende de dicho servicio. Además en este ejemplo declaramos directamente userService como una variable privada y no es necesario realizar la asignación dentro del constructor.

Siendo objetivos, esto es de nuevo un punto a favor de TypeScript. Es cierto, también, que si eres reacio a usar TypeScript pero quieres usar este mecanismo para facilitar la DI, el plugin de Babel angular2 annotations es lo que estás buscando.

TypeScript simplifica la Dependency Injection de Angular 2, pero también puedes conseguirlo con el plugin de Babel angular2 annotations.

Analizando los datos con la cabeza fría

TypeScript es ES6 con algunas mejoras que simplifican las cosas al trabajar con Angular 2.

Si eres reticente a abandonar el estándar, es importante destacar que alguna de las mejoras, como lo decoradores, se quiere incluir en ES7, el siguiente estandar de Javascript.

Considerando lo que ofrece TypeScript, usar ES6 puro con Angular 2 es complicarse la vida innecesariamente. Puedes obtener los mismos beneficios que aporta TypeScript con algunos plugins de Babel, pero ¿no es eso, de nuevo, salirse de ES6 puro? Mi consejo ante este dilema… puestos a emular TypeScript, mejor, utiliza TypeScript directamente.

Considerando TypeScript, usar ES6 puro con Angular 2 es complicarse la vida innecesariamente.

Conclusiones

A lo largo de este artículo he desgranado 3 motivos interesantes por los cuales creo que debes considerar TypeScript como primera opción a la hora de trabajar con Angular 2:

  • Detección temprana de errores en fase de compilación
  • Simplificación de código con Decoradores
  • Simplificación de la Inyección de Dependencias

Creo que debes considerar TypeScript como primera opción para Angular 2

Esta es mi opinión personal, pero también es la opinión de mucha gente de peso en el mundo de Angular 2. Recientemente hablaba del tema con Josh Morony – referencia en Ionic 2-, y además de corroborar mi punto de vista, recalcaba la importancia de que la comunidad Angular se está moviendo hacia TypeScript.

¿Y tú, Angulero, qué opinas? ¿Te pasas a TypeScript o prefieres ir a contracorriente?