Buscar..


Introducción

TypeScript, como ECMA Script 6, admite la programación orientada a objetos mediante clases. Esto contrasta con las versiones anteriores de JavaScript, que solo son compatibles con la cadena de herencia basada en prototipos.

El soporte de clase en TypeScript es similar al de lenguajes como Java y C #, ya que las clases pueden heredar de otras clases, mientras que los objetos se instancian como instancias de clase.

También similar a esos lenguajes, las clases de TypeScript pueden implementar interfaces o hacer uso de genéricos.

Clase simple

class Car {
    public position: number = 0;
    private speed: number = 42;
    
    move() {
        this.position += this.speed;
    }
}    

En este ejemplo, declaramos una clase simple de Car . La clase tiene tres miembros: una speed propiedad privada, una position propiedad pública y un move método público. Tenga en cuenta que cada miembro es público por defecto. Es por eso que move() es público, incluso si no usamos la palabra clave public .

var car = new Car();        // create an instance of Car
car.move();                 // call a method
console.log(car.position);  // access a public property

Herencia básica

class Car {
    public position: number = 0;
    protected speed: number = 42;
    
    move() {
        this.position += this.speed;
    }
}  

class SelfDrivingCar extends Car {

    move() {
        // start moving around :-)
        super.move();
        super.move();
    }
}

Este ejemplo muestra cómo crear una subclase muy sencilla del Car clase utilizando el extends palabra clave. La clase SelfDrivingCar anula el método move() y usa la implementación de la clase base usando super .

Constructores

En este ejemplo, usamos el constructor para declarar una position propiedad pública y una speed propiedad protegida en la clase base. Estas propiedades se denominan propiedades de parámetros . Nos permiten declarar un parámetro de constructor y un miembro en un solo lugar.

Una de las mejores cosas en TypeScript, es la asignación automática de parámetros del constructor a la propiedad relevante.

class Car {
    public position: number;        
    protected speed: number;

    constructor(position: number, speed: number) {
        this.position = position;
        this.speed = speed;
    }
    
    move() {
        this.position += this.speed;
    }        
}

Todo este código se puede resumir en un único constructor:

class Car {
    constructor(public position: number, protected speed: number) {}
    
    move() {
        this.position += this.speed;
    }        
}

Y ambos serán transpilados de TypeScript (tiempo de diseño y tiempo de compilación) a JavaScript con el mismo resultado, pero escribiendo mucho menos código:

var Car = (function () {
    function Car(position, speed) {
        this.position = position;
        this.speed = speed;
    }
    Car.prototype.move = function () {
        this.position += this.speed;
    };
    return Car;
}());

Los constructores de clases derivadas tienen que llamar al constructor de la clase base con super() .

class SelfDrivingCar extends Car {
    constructor(startAutoPilot: boolean) {
        super(0, 42);
        if (startAutoPilot) {
            this.move();
        }
    }
}

let car = new SelfDrivingCar(true);
console.log(car.position);  // access the public property position

Accesorios

En este ejemplo, modificamos el ejemplo de "clase simple" para permitir el acceso a la propiedad de speed . Los descriptores de acceso de typcript nos permiten agregar código adicional en getters o setters.

class Car {
    public position: number = 0;
    private _speed: number = 42;
    private _MAX_SPEED = 100
    
    move() {
        this.position += this._speed;
    }
    
    get speed(): number {
        return this._speed;
    }

    set speed(value: number) {
        this._speed = Math.min(value, this._MAX_SPEED);
    }
}

let car = new Car();
car.speed = 120;
console.log(car.speed);  // 100

Clases abstractas

abstract class Machine {
    constructor(public manufacturer: string) {
    }

    // An abstract class can define methods of it's own, or...
    summary(): string {
        return `${this.manufacturer} makes this machine.`;
    }
    
    // Require inheriting classes to implement methods
    abstract moreInfo(): string;
}

class Car extends Machine {
    constructor(manufacturer: string, public position: number, protected speed: number) {
        super(manufacturer);
    }
    
    move() {
        this.position += this.speed;
    }
    
    moreInfo() {
        return `This is a car located at ${this.position} and going ${this.speed}mph!`;
    }
}

let myCar = new Car("Konda", 10, 70);
myCar.move(); // position is now 80
console.log(myCar.summary()); // prints "Konda makes this machine."
console.log(myCar.moreInfo()); // prints "This is a car located at 80 and going 70mph!"

Las clases abstractas son clases base a partir de las cuales se pueden extender otras clases. No se pueden crear instancias de ellos mismos (es decir, no se puede hacer una new Machine("Konda") ).

Las dos características clave de una clase abstracta en Typescript son:

  1. Pueden implementar métodos propios.
  2. Pueden definir métodos que las clases heredadas deben implementar.

Por esta razón, las clases abstractas pueden considerarse conceptualmente como una combinación de una interfaz y una clase .

Monkey parchea una función en una clase existente

A veces es útil poder extender una clase con nuevas funciones. Por ejemplo, supongamos que una cadena se debe convertir en una cadena de caja de camello. Así que debemos decirle a TypeScript que String contiene una función llamada toCamelCase , que devuelve una string .

interface String {
    toCamelCase(): string;
}

Ahora podemos parchear esta función en la implementación de String .

String.prototype.toCamelCase = function() : string {
    return this.replace(/[^a-z ]/ig, '')
        .replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, (match: any, index: number) => {
            return +match === 0 ? "" : match[index === 0 ? 'toLowerCase' : 'toUpperCase']();
        });
}

Si esta extensión de String está cargada, se puede usar así:

"This is an example".toCamelCase();    // => "thisIsAnExample"

Transpilacion

Dada una clase SomeClass , veamos cómo se transpila el TypeScript a JavaScript.

Fuente de TypeScript

class SomeClass {

    public static SomeStaticValue: string = "hello";
    public someMemberValue: number = 15;
    private somePrivateValue: boolean = false;

    constructor () {
        SomeClass.SomeStaticValue = SomeClass.getGoodbye();
        this.someMemberValue = this.getFortyTwo();
        this.somePrivateValue = this.getTrue();
    }

    public static getGoodbye(): string {
        return "goodbye!";
    }

    public getFortyTwo(): number {
        return 42;
    }

    private getTrue(): boolean {
        return true;
    }

}

Fuente de JavaScript

Cuando se transpila utilizando TypeScript v2.2.2 , la salida es así:

var SomeClass = (function () {
    function SomeClass() {
        this.someMemberValue = 15;
        this.somePrivateValue = false;
        SomeClass.SomeStaticValue = SomeClass.getGoodbye();
        this.someMemberValue = this.getFortyTwo();
        this.somePrivateValue = this.getTrue();
    }
    SomeClass.getGoodbye = function () {
        return "goodbye!";
    };
    SomeClass.prototype.getFortyTwo = function () {
        return 42;
    };
    SomeClass.prototype.getTrue = function () {
        return true;
    };
    return SomeClass;
}());
SomeClass.SomeStaticValue = "hello";

Observaciones

  • La modificación del prototipo de la clase está dentro de un IIFE .
  • Las variables miembro se definen dentro de la function clase principal.
  • Las propiedades estáticas se agregan directamente al objeto de clase, mientras que las propiedades de instancia se agregan al prototipo.


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow