Clases: Atributos virtuales con getters y setters

Los getters y setters permiten fabricar atributos virtuales donde acceder al atributo ejecuta una función que devuelve el valor del atributo, y establecer el valor del atributo ejecuta otra que permitirá modificarlo.

Cuando trabajamos con clases en TypeScript, a menudo nos encontramos con la necesidad de manejar atributos que no se almacenan directamente, sino que se derivan de otros datos. Estos son los llamados atributos virtuales, y para gestionarlos de forma elegante y eficiente, TypeScript nos ofrece los getters y setters.

Imaginemos que tenemos una clase Rectangulo con atributos como ancho y alto. Queremos obtener el área, que es un valor derivado de esos dos atributos. Una forma inicial sería definir un método que calcule el área, pero esto nos obliga a llamarlo como una función, con paréntesis, lo que puede resultar menos intuitivo. Otra opción es calcular el área en el constructor y almacenarla, pero esto tiene inconvenientes: cada vez que creamos una instancia, calculamos el área aunque no la necesitemos, y además, si modificamos ancho o alto después, el área almacenada puede quedar desactualizada.

Para resolver esto, podemos usar un getter. Un getter es una función que se define con la palabra clave get y que se comporta externamente como un atributo. Esto significa que podemos acceder a area como si fuera una propiedad, pero en realidad se ejecuta código para calcular su valor en el momento. Por ejemplo:

class Rectangulo {
  ancho: number;
  alto: number;

  constructor(ancho: number, alto: number) {
    this.ancho = ancho;
    this.alto = alto;
  }

  get area(): number {
    return this.ancho * this.alto;
  }
}

const r1 = new Rectangulo(10, 5);
console.log(r1.area); // 50

Si añadimos un console.log dentro del getter, podemos ver que cada vez que accedemos a area, se ejecuta la función, lo que confirma que no es un atributo almacenado, sino derivado.

Además de los getters, existen los setters, que nos permiten definir qué ocurre cuando asignamos un valor a un atributo virtual. Por defecto, un getter sin setter se comporta como una propiedad de solo lectura, y si intentamos asignarle un valor, TypeScript nos dará un error. Pero si definimos un setter, podemos controlar la asignación y ejecutar código adicional, como validaciones, notificaciones o actualizaciones en bases de datos.

Para ilustrar esto, podemos crear un atributo virtual nombre en la clase Rectangulo. Internamente, almacenamos el valor en un atributo privado _nombre, y definimos un getter y un setter para nombre que gestionan el acceso y la modificación:

class Rectangulo {
  ancho: number;
  alto: number;
  private _nombre: string = '';

  constructor(ancho: number, alto: number) {
    this.ancho = ancho;
    this.alto = alto;
  }

  get area(): number {
    return this.ancho * this.alto;
  }

  get nombre(): string {
    console.log('Obtengo el nombre');
    return this._nombre;
  }

  set nombre(value: string) {
    console.log('Seteo el nombre');
    this._nombre = value;
  }
}

const r1 = new Rectangulo(10, 5);
r1.nombre = 'Rectángulo A'; // Se ejecuta el setter y muestra "Seteo el nombre"
console.log(r1.nombre);     // Se ejecuta el getter y muestra "Obtengo el nombre" seguido del valor

Este patrón es muy útil para encapsular la lógica relacionada con la lectura y escritura de propiedades, permitiéndonos ejecutar código adicional cuando se accede o modifica un atributo, sin que el usuario de la clase tenga que preocuparse por ello. Por ejemplo, podríamos hacer que al cambiar el nombre se actualice una base de datos o se notifique a otros componentes del sistema.

En definitiva, los getters y setters nos permiten crear atributos virtuales que se comportan como propiedades normales desde el exterior, pero que internamente pueden ejecutar funciones complejas para derivar valores o controlar modificaciones. Esto mejora la encapsulación y la flexibilidad de nuestras clases en TypeScript.

Lista de reproducción
  1. 1
    Temporada 1
    5 minutos
  2. 2
    ¿Qué es TypeScript?
    11 minutos
  3. 3
    Instalando TypeScript
    8 minutos
  4. 4
    Compilando un Hola Mundo sencillo
    7 minutos
  5. 5
    Hola Mundo pero con tipos
    10 minutos
  6. 6
    Tipos: tipos primitivos
    12 minutos
  7. 7
    Tipos: tipos especiales (any, null, ...)
    10 minutos
  8. 8
    Tipos: arrays y tuplas
    11 minutos
  9. 9
    Tipos: objetos
    7 minutos
  10. 10
    Funciones: lo básico
    9 minutos
  11. 11
    Funciones: tipando funciones
    9 minutos
  12. 12
    Clases: introducción a las clases
    9 minutos
  13. 13
    Clases: creando una clase
    10 minutos
  14. 14
    Clases: modificador private
    8 minutos
  15. 15
    Clases: modificador readonly
    3 minutos
  16. 16
    Clases: Atributos virtuales con getters y setters
    10 minutos
  17. 17
    Clases: herencia
    9 minutos
  18. 18
    Clases: modificadores abstract y protected
    8 minutos
  19. 19
    Tipos alias
    6 minutos
  20. 20
    Tipos literales
    5 minutos
  21. 21
    Uniones de tipos
    7 minutos
  22. 22
    Uniones discriminantes
    7 minutos
  23. 23
    Intersecciones de tipos
    5 minutos
  24. 24
    Interfaces: introducción
    7 minutos
  25. 25
    Interfaces: modificadores y funciones
    9 minutos
  26. 26
    Interfaces: usándolas con clases
    8 minutos
  27. 27
    Interfaces: herencia de interfaces
    8 minutos
  28. 28
    Interfaces: interfaces indizadas
    5 minutos
  29. 29
    Interfaces: funciones y tipos híbridos
    5 minutos
  30. 30
    ¿Qué diferencia hay entre interfaces y tipos? (2020)
    8 minutos
  31. 31
    Casteos con as
    6 minutos
  32. 32
    instanceof y las guardas
    9 minutos
  33. 33
    Tipos enumerados
    8 minutos
  34. 34
    Valores avanzados para enumerados
    7 minutos
  35. 35
    Enumerados con valores computados
    6 minutos
  36. 36
    Genéricos en tipos
    8 minutos
  37. 37
    Múltiples genéricos y buenas prácticas
    5 minutos
  38. 38
    Genéricos en funciones
    8 minutos
  39. 39
    Genéricos con restricciones
    6 minutos
  40. 40
    Tipos de utilidad
    3 minutos
  41. 41
    Exportando módulos
    9 minutos
  42. 42
    Importando módulos
    7 minutos
  43. 43
    Export default e import asterisco
    6 minutos
  44. 44
    tsconfig
    7 minutos
  45. 45
    Módulos desde NPM
    7 minutos
  46. 46
    Arroba types y los .d.ts
    8 minutos
  47. 47
    Ejemplo (1): creando una API REST simple en TypeScript
    11 minutos
  48. 48
    Ejemplo (2): montando un servidor Express
    8 minutos
  49. 49
    Ejemplo (3): haciendo las funciones de control de datos
    11 minutos
  50. 50
    Ejemplo (4): conectando todas las piezas
    7 minutos