Skip to content

Classes - Class en TypeScript

En TypeScript, una clase es una plantilla para crear un objeto. Una clase encapsula datos para el objeto y métodos para manipular los datos. En TypeScript, una clase se define utilizando la palabra clave class.

Recursos

  • doc ts oficial
  • POO en vanilla Javascript (sin TS) 👇🏽👇🏽👇🏽

Sintaxis de una clase

  • Declara una clase con la palabra clave class.
  • El nombre de la clase debe comenzar con una letra mayúscula (por convención).
  • El cuerpo de la clase está entre llaves {}.
  • Las propiedades y métodos de la clase se definen dentro del cuerpo de la clase.
  • En este ejemplo, name y age son propiedades públicas de la clase Pet.
ts
class Pet {
  name: string;
  age: number;
}

const myPet = new Pet();
myPet.name = "Tommy";
myPet.age = 5;

console.log(myPet.name); // Tommy

Property 'name' has no initializer and is not definitely assigned in the constructor.

Este error puede ocurrir con TypeScript 2.7 en modo "estricto". TypeScript 2.7 introdujo un nuevo indicador llamado --strictPropertyInitialization, que le indica al compilador que verifique que cada propiedad de instancia de una clase se inicialice en el cuerpo del constructor o mediante un inicializador de propiedad.

más info aquí

assertion operator !

En términos simples, el operador de aserción (!) en TypeScript le dice al compilador que una propiedad o variable no es null ni undefined. Esto se usa para evitar errores de tipo en tiempo de compilación cuando se está seguro de que una propiedad existe, aunque TypeScript no pueda inferirlo automáticamente.

ts
class Pet {
  name!: string;
  age!: number;
}

Al usar este operador, le estás diciendo al compilador que confíe en ti sobre la existencia de una propiedad o variable, lo cual puede ser peligroso si no estás completamente seguro de que no es null ni undefined.

ts
let myValue: string | undefined;

// Si myValue es undefined, esto causará un error en tiempo de ejecución
console.log(myValue!.toUpperCase());

En este caso, si myValue es undefined, intentar llamar a toUpperCase() resultará en un error en tiempo de ejecución.

Puedes usar el operador de coalescencia nula (??):

ts
let myValue: string | undefined = undefined;
console.log((myValue ?? "Valor predeterminado").toUpperCase());

Propiedades de una clase

Las propiedades de una clase son variables que se declaran dentro de una clase. Las propiedades de una clase pueden ser públicas (public), privadas (private), protegidas (protected) o de solo lectura (readonly).

Ejemplo:

ts
class Pet {
  // Propiedad pública
  name: string;

  // Propiedad privada
  private age: number;

  // Propiedad protegida
  protected breed: string;

  // Propiedad de solo lectura
  readonly color: string = "Brown";
}

Private vs Protected

  • Private: Las propiedades privadas solo se pueden acceder dentro de la clase en la que se declaran.
  • Protected: Las propiedades protegidas se pueden acceder dentro de la clase en la que se declaran y en las clases derivadas.

Esto lo veremos más adelante en la sección de herencia.

readonly

En TypeScript, puedes declarar una propiedad de solo lectura utilizando la palabra clave readonly. Una propiedad de solo lectura solo se puede asignar un valor cuando se inicializa o en el constructor de la clase. No en ambos.

ts
class Pet {
  readonly name: string = "Tommy";
}

const myPet = new Pet("Tommy");

// Esto dará un error en tiempo de compilación
myPet.name = "Buddy";

Constructores

Un constructor es un método especial que se llama automáticamente cuando se crea un objeto de una clase. En TypeScript, un constructor se define utilizando la palabra clave constructor.

Ejemplo:

ts
class Pet {
  public name: string;
  private age: number;
  protected breed = "Unknown";

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  public getDescription(): string {
    return `${this.name} is ${this.age} years old.`;
  }
}

const myPet = new Pet("Buddy", 3);

console.log(myPet.getDescription()); // Output: Buddy is 3 years old.

En términos muy sencillos, this en TypeScript (y en otros lenguajes orientados a objetos) se refiere al objeto actual en el que se está ejecutando el código. Es una forma de acceder a las propiedades y métodos del objeto desde dentro de sus propios métodos.

En tu método getDescription, this se refiere a la instancia actual de la clase Pet. Así es como puedes acceder a las propiedades name y age de esa instancia.

Es obligatorio utilizar this en TypeScript (y en otros lenguajes orientados a objetos) para referirse a las propiedades y métodos del objeto actual porque:

  • Claridad y Desambiguación: this deja claro que estás accediendo a una propiedad o método del objeto actual. Sin this, el compilador no sabría si te refieres a una variable local, un parámetro del método o una propiedad del objeto.
  • Contexto del Objeto: this proporciona el contexto del objeto en el que se está ejecutando el código. Esto es especialmente importante en métodos de instancia donde necesitas acceder a las propiedades del objeto.

Constructor abreviado

En TypeScript, puedes abreviar la declaración de propiedades y el constructor utilizando el modificador de acceso en el constructor.

Ejemplo:

ts
class Pet {
  constructor(
    public name: string,
    private age: number,
    protected breed = "Unknown"
  ) {}

  public getDescription(): string {
    return `${this.name} - ${this.breed} is ${this.age} years old.`;
  }
}

const myPet = new Pet("Buddy", 3, "Golden Retriever");

console.log(myPet.getDescription()); // Output: Buddy - Golden Retriever is 3 years old.

En este ejemplo, las propiedades name, age y breed se declaran en el constructor. Esto es una forma abreviada de declarar propiedades y asignarles valores en el constructor.

Herencia

La herencia es un concepto en el que una clase (subclase) hereda propiedades y métodos de otra clase (superclase). En TypeScript, puedes lograr la herencia utilizando la palabra clave extends.

Ejemplo:

ts
class Pet {
  constructor(
    public name: string,
    private age: number,
    readonly color: string,
    protected breed = "Unknown"
  ) {}

  public getDescription(): string {
    return `${this.name} - ${this.breed} is ${this.age} years old and has ${this.color} fur.`;
  }
}

class Dog extends Pet {
  constructor(name: string, age: number, color: string, breed: string) {
    // super se usa para llamar al constructor de la clase padre
    // se debe respetar la firma del constructor de la clase padre
    super(name, age, color, breed);
  }

  public bark(): void {
    console.log("Woof! Woof!");
  }
}

class Cat extends Pet {
  constructor(name: string, age: number, color: string) {
    super(name, age, color);
  }

  public meow(): void {
    console.log("Meow! Meow!");
  }
}

const dog = new Dog("Buddy", 5, "Golden", "Golden Retriever");
console.log(dog);
dog.bark();

const cat = new Cat("Whiskers", 3, "White");
console.log(cat);
cat.meow();

En este ejemplo, la clase Dog y la clase Cat heredan de la clase Pet. La clase Dog hereda las propiedades y métodos de la clase Pet y también define un método adicional bark. La clase Cat hereda las propiedades y métodos de la clase Pet y también define un método adicional meow.

Polimorfismo y Private Properties

ts
class Dog extends Pet {
  constructor(name: string, age: number, color: string, breed: string) {
    super(name, age, color, breed);
  }

  public bark(): void {
    console.log("Woof! Woof!");
  }

  // Sobrescribe el método getDescription de la clase Pet
  // Esto se conoce como "polimorfismo"
  public getDescription(): string {
    // super tambien se puede usar para acceder a los métodos de la clase padre
    return `${super.getDescription()} It has a ${this.breed} breed.`;
  }

  // Aquí se genera un error porque la propiedad age es privada en la clase Pet
  // private solo se puede acceder dentro de la clase en la que se declara
  public getAge(): number {
    return this.age;
  }
}

Getter y Setter

En TypeScript, puedes definir métodos especiales llamados getters y setters (también posible en JS) para acceder y modificar propiedades de una clase.

ts
class Cat extends Pet {
  constructor(
    name: string,
    age: number,
    color: string,
    readonly lifes: number
  ) {
    super(name, age, color);
  }

  public meow(): void {
    console.log("Meow! Meow!");
  }

  // Getter: Obtiene el valor de la propiedad lifes
  // Se puede acceder a la propiedad lifes como si fuera una propiedad
  // Cómo regla no puede recibir parámetros
  get getLifes(): number {
    return this.lifes;
  }

  // Setter: Establece el valor de la propiedad lifes
  // Se puede establecer la propiedad lifes como si fuera una propiedad
  // Cómo regla solo puede recibir un parámetro
  set setLifes(lifes: number) {
    this.lifes = lifes;
  }
}

const cat = new Cat("Whiskers", 3, "White", 7);

// Esto nos da la posibilidad de cambiar el valor de la propiedad lifes a través del setter
cat.setLifes = 8;
console.log(cat);

// Esto nos da la posibilidad de obtener el valor de la propiedad lifes a través del getter
console.log(cat.getLifes);

Clases Abstractas

En TypeScript, una clase abstracta es una clase que no se puede instanciar directamente. Se utiliza como plantilla para otras clases que extienden la clase abstracta.

ts
abstract class Pet {
  constructor(
    public name: string,
    private age: number,
    readonly color: string,
    protected breed = "Unknown"
  ) {}

  public getDescription(): string {
    return `${this.name} - ${this.breed} is ${this.age} years old and has ${this.color} fur.`;
  }
}

En este ejemplo, la clase Pet es una clase abstracta. No se puede instanciar directamente, pero se puede utilizar como plantilla para otras clases que extienden la clase Pet.

Métodos Estáticos

En TypeScript, un método estático es un método que se puede llamar en la clase misma, en lugar de en una instancia de la clase. Se definen utilizando la palabra clave static.

ts
class Pet {
  constructor(
    public name: string,
    private age: number,
    readonly color: string,
    protected breed = "Unknown"
  ) {}

  public getDescription(): string {
    return `${this.name} - ${this.breed} is ${this.age} years old and has ${this.color} fur.`;
  }

  static descriptionClass(): string {
    return "This class is used to create pets.";
  }
}

console.log(Pet.descriptionClass());

Implements

En TypeScript, las interfaces se utilizan para definir la forma de un objeto, especificando qué propiedades y métodos debe tener.

Solo está obligada a cumplir con el contrato mínimo definido por la interfaz. Esto significa que la clase debe tener al menos las propiedades y métodos especificados en la interfaz, pero puede tener propiedades y métodos adicionales.

Esto es útil para garantizar que las clases sigan una estructura específica y para habilitar el polimorfismo.

ts
interface Animal {
  name: string;
  age: number;
  getDescription(): string;
}

class Dog implements Animal {
  constructor(public name: string, public age: number, public breed: string) {}

  public getDescription(): string {
    return `${this.name} is a Dog and is ${this.age} years old.`;
  }

  public bark(): void {
    console.log("Woof! Woof!");
  }
}

const dog = new Dog("Buddy", 5, "Golden Retriever");

Class vs Interface

Estas son algunas diferencias clave entre una interfaz y una clase en TypeScript:

Interfaces

  • Definición de Contrato: Una interfaz define un contrato que las clases pueden implementar. Especifica qué propiedades y métodos deben estar presentes, pero no proporciona implementaciones.
  • Sin Implementación: Las interfaces no pueden contener implementaciones de métodos ni inicializaciones de propiedades. Solo definen la estructura.

Clases

  • Definición y Implementación: Una clase define tanto la estructura (propiedades y métodos) como las implementaciones de esos métodos.
  • Inicialización de Propiedades: Las clases pueden inicializar valores por defecto para sus propiedades. Herencia: Las clases pueden extender (heredar de) una sola clase base, pero pueden implementar múltiples interfaces.
  • Instanciación: Las clases pueden ser instanciadas para crear objetos.

Resumen

En TypeScript, una clase es una plantilla para crear un objeto. Una clase encapsula datos para el objeto y métodos para manipular los datos. En TypeScript, una clase se define utilizando la palabra clave class.

  • Sintaxis de una clase: Una clase se declara con la palabra clave class. El cuerpo de la clase está entre llaves {}. Las propiedades y métodos de la clase se definen dentro del cuerpo de la clase.
  • Propiedades de una clase: Las propiedades de una clase son variables que se declaran dentro de una clase. Las propiedades de una clase pueden ser públicas (public), privadas (private), protegidas (protected) o de solo lectura (readonly).
  • Constructores: Un constructor es un método especial que se llama automáticamente cuando se crea un objeto de una clase. En TypeScript, un constructor se define utilizando la palabra clave constructor.
  • Herencia: La herencia es un concepto en el que una clase (subclase) hereda propiedades y métodos de otra clase (superclase). En TypeScript, puedes lograr la herencia utilizando la palabra clave extends.
  • Polimorfismo: El polimorfismo es la capacidad de una clase para proporcionar una implementación específica de un método que ya está definido en una de sus superclases. En TypeScript, puedes lograr el polimorfismo sobrescribiendo métodos de la clase padre en la clase hija.
  • Getter y Setter: En TypeScript, puedes definir métodos especiales llamados getters y setters para acceder y modificar propiedades de una clase.
  • Clases Abstractas: En TypeScript, una clase abstracta es una clase que no se puede instanciar directamente. Se utiliza como plantilla para otras clases que extienden la clase abstracta.
  • Métodos Estáticos: En TypeScript, un método estático es un método que se puede llamar en la clase misma, en lugar de en una instancia de la clase. Se definen utilizando la palabra clave static.
  • Implements: En TypeScript, las interfaces se utilizan para definir la forma de un objeto, especificando qué propiedades y métodos debe tener. Una clase puede implementar una o más interfaces.