Clases en TypeScript
En TypeScript, una clase es una plantilla para crear objetos que tienen propiedades y métodos comunes. Ya que TypeScript es un superconjunto de JavaScript soporta el uso de las clases agregadas en la especificación ES2015.
La programación orientada a objetos (POO) es un paradigma de programación cuyo objetivo es modelar objetos del mundo real y sus relaciones en componentes denominados clases. Si es to es nuevo para ti, te recomendamos pasar primero por Introducción a la Programación Orientada a Objetos.
Declaración de clase
En TypeScript podemos definir una clase de la misma manera como lo haríamos en JavaScript.
class Point {
x: number = 0;
y: number = 0;
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
}
Observa que por defecto TypeScript obliga a que se inicializen los atributos de clase. Este comportamiento hace parte del flag --strictPropertyInitialization
. Dado el anterior ejemplo podríamos utilizar el operador new y pasar los argumentos en el "constructor".
const pt = new Point(1, 2);
pt.x = 10;
console.log("X: " + pt.x + ', Y: ' + pt.y);
La salida sería la siguiente:
X: 10, Y: 2
Atributos de lectura
Es posible definir atributos que solo sean de lectura. Con esto, sería imposible tratar de hacer una reasignación a un atributo una vez inicializado. Si modificamos el anterior ejemplo podríamos configurar el atributo x
para que sea de solo lectura.
readonly x: number = 0;
Una vez se intente cambiar el valor de dicho atributo TypeScript lanzará un error.
Encapsulamiento
El encapsulamiento es un concepto de programación orientada a objetos que se refiere a la idea de ocultar los detalles internos de una clase o objeto y exponer solo lo que es necesario para su uso externo. TypeScript provee al igual que la mayoría de lenguajes de programación los tipos de acceso public, protected y private.
Public
La visibilidad por defecto de un miembro de clase es por defecto public
. Esto implica que es posible acceder a este miembro de clase (propiedad o método) desde cualquier parte.
class Point {
// atributos definidos como "public"
public x: number = 1;
public y: number = 1;
// método definido como "public"
public scale(n: number): void {
// acceso a propiedades internas
this.x *= n;
this.y *= n;
}
}
const pt = new Point();
// escalar con método de clase
pt.scale(3);
// escalar accediendo directamente a las propiedades
pt.x = 3;
pt.y = 3;
Protected
Los miembros de clase declarados como protected
pueden ser solamente accedidos desde la propia clase o desde subclases desde la que fueron declarados.
class Point {
// atributos definidos como "protected"
protected x: number = 1;
protected y: number = 1;
// método definido como "protected"
protected scale(n: number): void {
// acceso a propiedades internas
this.x *= n;
this.y *= n;
}
}
class SubPoint extends Point {
scale2x(): void {
// acceso a propiedades protegidas
this.x *= 2;
this.y *= 2;
}
scale3x(): void {
// acceso a método protegido
this.scale(3);
}
}
const pt = new Point();
pt.scale2x();
pt.scale3x();
El siguiente código arrojaría error debido a que estamos accediando a propiedades protegidas desde un lugar no permitido.
const pt = new SubPoint();
// acceso NO PERMITIDO a propiedades protegidas
pt.x = 3;
pt.y = 3;
// acceso NO PERMITIDO a método protegido
pt.scale(3);
Private
Los miembros de clase declarados como private
pueden ser solamente accedidos desde la clase en la cual fueron declarados.
class Point {
// atributos definidos como "private"
private x: number = 1;
private y: number = 1;
// método definido como "private"
protected scale(n: number): void {
// acceso a propiedades internas privadas
this.x *= n;
this.y *= n;
}
}
class SubPoint extends Point {
scale2x(): void {
// acceso NO PERMITIDO a propiedades protegidas
this.x *= 2;
this.y *= 2;
}
scale3x(): void {
// acceso NO PERMITIDO a método protegido
this.scale(3);
}
}
const pt = new Point();
// acceso NO PERMITIDO a propiedades protegidas
pt.x = 3;
pt.y = 3;
// acceso NO PERMITIDO a método protegido
pt.scale(3);
Métodos
Para crear métodos en las clases solo debemos agregar nuevas funciones después del constructor. Observa también como en el siguiente método utilizamos this
para referirnos a la instancia actual de la clase.
class Point {
x: number = 0;
y: number = 0;
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
scale(n: number): void {
this.x *= n;
this.y *= n;
}
}
Utilicemos el método anterior para escalar nuestra instancia en 3x.
const pt = new Point(2, 2);
pt.scale(3);
console.log("X: " + pt.x + ', Y: ' + pt.y);
La salida del anterior programa es la siguiente:
X: 6, Y: 6
Getters
Los getters no son más que métodos que pueden acceder a propiedades definidas en una clase. Generalmente se utilizan cuando no se puede acceder directamente a la propiedad o cuando su acceso involucra una lógica adicional.
class Point {
private x: number = 1;
getX(): string {
return 'X:' + this.x;
}
}
const pt = new Point();
console.log(pt.getX());
En este caso la manera de utilizar el getter es así:
const pt = new Point();
console.log(pt.getX());
Si bien podemos codificar getters de la manera tradicional, algunos lenguajes implementan los getters de una manera particular. En TypeScript podemos hacer uso de la palabra reservada get
. Reescribamos el anterior ejemplo para hacer uso de este feature de TypeScript.
class Point {
private _x: number = 1;
get x(): string {
return 'X:' + this._x;
}
}
De esta forma, el acceso al getter es como si se accediera a la propiedad directamente.
const pt = new Point();
console.log(pt.x);
Setters
Los setters no son más que métodos que pueden modificar a propiedades definidas en una clase. Generalmente se utilizan cuando no se puede acceder directamente a la propiedad o cuando su modificación involucra una lógica adicional.
class Point {
private x: number = 1;
setX(value: number): void {
if (value < 0)
throw new Error('invalid value for X');
this.x = value;
}
}
En este caso la manera de utilizar el setter es así:
const pt = new Point();
pt.setX(3);
Si bien podemos codificar setters de la manera tradicional, algunos lenguajes implementan los setters de una manera particular. En TypeScript podemos hacer uso de la palabra reservada set
. Reescribamos el anterior ejemplo para hacer uso de este feature de TypeScript.
class Point {
private _x: number = 1;
set x(value: number) {
if (value < 0)
throw new Error('invalid value for X');
this._x = value;
}
}
De esta forma, el acceso al setter es como si modificara la propiedad directamente en la instancia.
const pt = new Point();
pt.x = 3;