Fundamentos de clases
// Clase básica
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Hello, I'm ${this.name}`;
}
}
const person = new Person("Alice", 30);
console.log(person.greet());
// Atajo de propiedades en el constructor
class User {
constructor(
public name: string,
public email: string,
private password: string
) {}
}
// Equivalente a:
class UserVerbose {
public name: string;
public email: string;
private password: string;
constructor(name: string, email: string, password: string) {
this.name = name;
this.email = email;
this.password = password;
}
}
Modificadores de acceso
class BankAccount {
// public - accesible en cualquier lugar (por defecto)
public accountHolder: string;
// private - solo accesible dentro de la clase
private balance: number;
// protected - accesible en la clase y subclases
protected accountNumber: string;
// readonly - solo se asigna en el constructor
readonly createdAt: Date;
constructor(holder: string, initialBalance: number) {
this.accountHolder = holder;
this.balance = initialBalance;
this.accountNumber = this.generateAccountNumber();
this.createdAt = new Date();
}
private generateAccountNumber(): string {
return Math.random().toString(36).substring(2, 12);
}
public deposit(amount: number): void {
if (amount > 0) {
this.balance += amount;
}
}
public getBalance(): number {
return this.balance;
}
}
const account = new BankAccount("Alice", 1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// account.balance; // Error: private
// account.accountNumber; // Error: protected
Herencia
class Animal {
constructor(public name: string) {}
move(distance: number = 0): void {
console.log(`${this.name} moved ${distance} meters`);
}
speak(): void {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name); // Llamar al constructor padre
}
// Sobrescribir método del padre
speak(): void {
console.log(`${this.name} barks!`);
}
// Nuevo método
fetch(): void {
console.log(`${this.name} fetches the ball`);
}
}
class Cat extends Animal {
speak(): void {
super.speak(); // Llamar método del padre
console.log(`${this.name} meows!`);
}
}
const dog = new Dog("Max", "Labrador");
dog.speak(); // "Max barks!"
dog.move(10); // "Max moved 10 meters"
dog.fetch(); // "Max fetches the ball"
Clases abstractas
// Clase abstracta - no se puede instanciar directamente
abstract class Shape {
constructor(public color: string) {}
// Método abstracto - debe ser implementado por subclases
abstract getArea(): number;
abstract getPerimeter(): number;
// Método concreto - implementación compartida
describe(): string {
return `A ${this.color} shape with area ${this.getArea()}`;
}
}
class Circle extends Shape {
constructor(color: string, public radius: number) {
super(color);
}
getArea(): number {
return Math.PI * this.radius ** 2;
}
getPerimeter(): number {
return 2 * Math.PI * this.radius;
}
}
class Rectangle extends Shape {
constructor(
color: string,
public width: number,
public height: number
) {
super(color);
}
getArea(): number {
return this.width * this.height;
}
getPerimeter(): number {
return 2 * (this.width + this.height);
}
}
// const shape = new Shape("red"); // Error: no se puede instanciar una clase abstracta
const circle = new Circle("red", 5);
const rectangle = new Rectangle("blue", 4, 6);
console.log(circle.describe());
console.log(rectangle.getArea());
Implementar interfaces
interface Printable {
print(): void;
}
interface Saveable {
save(): Promise<void>;
load(): Promise<void>;
}
// Implementar una interfaz
class Document implements Printable {
constructor(public content: string) {}
print(): void {
console.log(this.content);
}
}
// Implementar múltiples interfaces
class CloudDocument implements Printable, Saveable {
constructor(public content: string, private id: string) {}
print(): void {
console.log(this.content);
}
async save(): Promise<void> {
console.log(`Saving document ${this.id}`);
}
async load(): Promise<void> {
console.log(`Loading document ${this.id}`);
}
}
// Interface con restricción de clase
interface Repository<T> {
findById(id: string): Promise<T | null>;
save(item: T): Promise<void>;
delete(id: string): Promise<void>;
}
class UserRepository implements Repository<User> {
async findById(id: string): Promise<User | null> {
return null;
}
async save(user: User): Promise<void> {}
async delete(id: string): Promise<void> {}
}
Miembros estáticos
class MathUtils {
// Propiedad estática
static readonly PI = 3.14159;
// Propiedad estática privada
private static instance: MathUtils | null = null;
// Método estático
static add(a: number, b: number): number {
return a + b;
}
static multiply(a: number, b: number): number {
return a * b;
}
// Patrón singleton
static getInstance(): MathUtils {
if (!MathUtils.instance) {
MathUtils.instance = new MathUtils();
}
return MathUtils.instance;
}
// Bloque estático (ES2022)
static {
console.log("MathUtils class initialized");
}
}
// Uso - no necesitas instanciar
console.log(MathUtils.PI);
console.log(MathUtils.add(2, 3));
console.log(MathUtils.multiply(4, 5));
Getters y setters
class Temperature {
private _celsius: number = 0;
// Getter
get celsius(): number {
return this._celsius;
}
// Setter con validación
set celsius(value: number) {
if (value < -273.15) {
throw new Error("Temperature below absolute zero!");
}
this._celsius = value;
}
// Propiedad calculada
get fahrenheit(): number {
return (this._celsius * 9) / 5 + 32;
}
set fahrenheit(value: number) {
this._celsius = ((value - 32) * 5) / 9;
}
get kelvin(): number {
return this._celsius + 273.15;
}
}
const temp = new Temperature();
temp.celsius = 25;
console.log(temp.fahrenheit); // 77
console.log(temp.kelvin); // 298.15
temp.fahrenheit = 100;
console.log(temp.celsius); // 37.78
Puntos clave
- • Usa modificadores de acceso: public, private, protected, readonly
- • Atajo de propiedades en parámetros del constructor
- • Las clases abstractas definen contratos para subclases
- • Las clases pueden implementar múltiples interfaces
- • Los miembros estáticos pertenecen a la clase, no a instancias
- • Getters/setters controlan el acceso a propiedades