CAP 09 · LEC 06·Programación orientada a objetos

Getters y setters: propiedades inteligentes

Los getters y setters permiten acceder a propiedades con lógica personalizada — cálculos automáticos, validaciones y transformaciones — con la sintaxis natural de una propiedad.

● INTERMEDIO7 min lectura4 ejerciciospor Fernando Herrera · actualizado mayo de 2026
¿Encontraste un error o algo que mejorar?Editá esta lección en GitHub →

get: propiedades computadas de solo lectura

Un getter define una propiedad que se calcula a partir de otras. Se accede como propiedad, no como método (sin paréntesis):

class Rectangulo { constructor(ancho, alto) { this.ancho = ancho; this.alto = alto; } // get convierte un método en una propiedad computada get area() { return this.ancho * this.alto; } get perimetro() { return 2 * (this.ancho + this.alto); } get esCuadrado() { return this.ancho === this.alto; } } const r = new Rectangulo(10, 5); // Se accede sin paréntesis, como si fueran propiedades normales console.log(r.area); // 50 console.log(r.perimetro); // 30 console.log(r.esCuadrado); // false // Si cambia la propiedad base, el getter se recalcula r.ancho = 5; console.log(r.area); // 25 console.log(r.esCuadrado); // true
class Rectangulo { constructor( public ancho: number, public alto: number ) {} get area(): number { return this.ancho * this.alto; } get perimetro(): number { return 2 * (this.ancho + this.alto); } get esCuadrado(): boolean { return this.ancho === this.alto; } } const r = new Rectangulo(10, 5); console.log(r.area); // 50 console.log(r.perimetro); // 30 console.log(r.esCuadrado); // false r.ancho = 5; console.log(r.area); // 25 console.log(r.esCuadrado); // true
Salida50 30 false 25 true
Getter vs método

r.area (getter) vs r.getArea() (método) hacen lo mismo, pero el getter se usa cuando el resultado parece una propiedad del objeto — algo que el objeto es, no algo que hace.

set: interceptar asignaciones

Un setter intercepta las asignaciones a una propiedad. Puedes transformar el valor, validarlo o disparar efectos secundarios:

class Usuario { #_nombre; #_email; constructor(nombre, email) { // Los setters también se llaman en el constructor this.nombre = nombre; this.email = email; } get nombre() { return this.#_nombre; } set nombre(valor) { if (!valor || valor.trim().length < 2) { throw new Error("El nombre debe tener al menos 2 caracteres"); } this.#_nombre = valor.trim(); } get email() { return this.#_email; } set email(valor) { if (!valor.includes("@")) { throw new Error("Email inválido"); } this.#_email = valor.toLowerCase(); } } const u = new Usuario(" Ana ", "ANA@EJEMPLO.COM"); console.log(u.nombre); // "Ana" ← trimmed console.log(u.email); // "ana@ejemplo.com" ← lowercase // ❌ Esto lanzará un error // u.nombre = "A";
class Usuario { private _nombre: string = ""; private _email: string = ""; constructor(nombre: string, email: string) { this.nombre = nombre; this.email = email; } get nombre(): string { return this._nombre; } set nombre(valor: string) { if (!valor || valor.trim().length < 2) { throw new Error("El nombre debe tener al menos 2 caracteres"); } this._nombre = valor.trim(); } get email(): string { return this._email; } set email(valor: string) { if (!valor.includes("@")) { throw new Error("Email inválido"); } this._email = valor.toLowerCase(); } } const u = new Usuario(" Ana ", "ANA@EJEMPLO.COM"); console.log(u.nombre); // "Ana" console.log(u.email); // "ana@ejemplo.com"
SalidaAna ana@ejemplo.com
Convención _propiedad

La convención _nombre (con guión bajo) indica que la propiedad es de uso interno y el acceso externo debe hacerse a través del getter/setter. En TypeScript moderno también puedes usar #nombre (privado real de JS).

Propiedades calculadas: fullName y más

Los getters son ideales para propiedades que se derivan de combinar otras propiedades:

class Persona { constructor(nombre, apellido, nacimiento) { this.nombre = nombre; this.apellido = apellido; this.nacimiento = new Date(nacimiento); } get nombreCompleto() { return `${this.nombre} ${this.apellido}`; } get edad() { const hoy = new Date(); const cumple = new Date(hoy.getFullYear(), this.nacimiento.getMonth(), this.nacimiento.getDate()); const edad = hoy.getFullYear() - this.nacimiento.getFullYear(); return hoy < cumple ? edad - 1 : edad; } get iniciales() { return `${this.nombre[0]}.${this.apellido[0]}.`; } } const p = new Persona("Ana", "García", "1995-06-15"); console.log(p.nombreCompleto); // "Ana García" console.log(p.iniciales); // "A.G."
class Persona { nombre: string; apellido: string; nacimiento: Date; constructor(nombre: string, apellido: string, nacimiento: string) { this.nombre = nombre; this.apellido = apellido; this.nacimiento = new Date(nacimiento); } get nombreCompleto(): string { return `${this.nombre} ${this.apellido}`; } get edad(): number { const hoy = new Date(); const cumple = new Date(hoy.getFullYear(), this.nacimiento.getMonth(), this.nacimiento.getDate()); const edad = hoy.getFullYear() - this.nacimiento.getFullYear(); return hoy < cumple ? edad - 1 : edad; } get iniciales(): string { return `${this.nombre[0]}.${this.apellido[0]}.`; } } const p = new Persona("Ana", "García", "1995-06-15"); console.log(p.nombreCompleto); // "Ana García" console.log(p.iniciales); // "A.G."
SalidaAna García A.G.

Validación en setters

Los setters son el lugar ideal para centralizar las reglas de negocio sobre qué valores son válidos:

class Producto { #nombre; #precio; #stock; constructor(nombre, precio, stock) { this.nombre = nombre; this.precio = precio; this.stock = stock; } get precio() { return this.#precio; } set precio(valor) { if (typeof valor !== "number" || valor < 0) { throw new TypeError("El precio debe ser un número positivo"); } this.#precio = Math.round(valor * 100) / 100; // 2 decimales } get stock() { return this.#stock; } set stock(valor) { if (!Number.isInteger(valor) || valor < 0) { throw new RangeError("El stock debe ser un entero no negativo"); } this.#stock = valor; } get disponible() { return this.#stock > 0; } get resumen() { return `${this.#nombre} | $${this.#precio} | Stock: ${this.#stock}`; } } const laptop = new Producto("Laptop Pro", 1199.999, 15); console.log(laptop.precio); // 1200 ← redondeado console.log(laptop.disponible); // true console.log(laptop.resumen); // "Laptop Pro | $1200 | Stock: 15"
class Producto { private _nombre: string; private _precio: number = 0; private _stock: number = 0; constructor(nombre: string, precio: number, stock: number) { this._nombre = nombre; this.precio = precio; this.stock = stock; } get precio(): number { return this._precio; } set precio(valor: number) { if (valor < 0) throw new RangeError("El precio no puede ser negativo"); this._precio = Math.round(valor * 100) / 100; } get stock(): number { return this._stock; } set stock(valor: number) { if (!Number.isInteger(valor) || valor < 0) { throw new RangeError("El stock debe ser un entero no negativo"); } this._stock = valor; } get disponible(): boolean { return this._stock > 0; } get resumen(): string { return `${this._nombre} | $${this._precio} | Stock: ${this._stock}`; } } const laptop = new Producto("Laptop Pro", 1199.999, 15); console.log(laptop.precio); // 1200 console.log(laptop.disponible); // true console.log(laptop.resumen); // "Laptop Pro | $1200 | Stock: 15"
Salida1200 true Laptop Pro | $1200 | Stock: 15

Practica