Seguramente ya te has habituado al uso de const en TypeScript (y espero que en JavaScript también, no me declares constantes con let o var 😉). Y sabemos que con elementos de tipo primitivo funciona de lujo:

const NAME: string = "Juan";
// Esta sentencia da error, puesto que no puedes reasignar una primitiva declarada con const.
NAME = "Pepe";

Pero quizás te has visto alguna vez con el caso siguiente:

const person: IPerson = {
  name: "Juan",
  hobbies: ["nadar", "leer", "correr"]
};

// Esto no falla...
person.nombre = "Pepe";
person.hobbies = ["mirar al horizonte pensativo"];

Y es que el declarar como constante un objeto, lo que no permite es reasignar la referencia al objeto, pero sí podremos modificar el valor de sus atributos. Es decir, con el ejemplo superior, lo único que no podremos hacer es tener lo siguiente: person = {...} o person = anotherPerson.

Hoy veremos varias formas de poder declarar objetos como constantes, de forma que no podamos tampoco cambiar sus atributos. Eso sí, sólo desde TypeScript, en JavaScript no existen estas restricciones, por desgracia.

Método 1. Declarando un objeto como const

En este caso, lo que usaremos es const como tipo del objeto. De esta forma ya no será posible modificar sus atributos.

const person = {
 hobbies: [] as string[],
 name: "Pepito"
} as const; // Obsérvese su uso aquí

// Da el error: Cannot assign to 'name' because it is a read-only property.
person.name = "Hola";
// Da el error: Cannot assign to 'hobbies ' because it is a read-only property.
person.hobbies = ["leer"];

// Eso sí, podemos seguir añadiendo y eliminando elementos del array con sus propios métodos.
person.contents.push("escribir");

Método 2. Usando readonly

Si usamos el modificador readonly en un atributo de una clase, un tipo, o una interfaz. Éste pasará a ser de sólo lectura.

// Ejemplo con tipos
// -----------------
type PersonType = {
  age: number;
  readonly hobbies: string[];
  readonly name: string;
};

const person: PersonType = {
  age: 38
  hobbies: ["leer"],
  name: "Pepito"
};

// Da el error: Cannot assign to 'name' because it is a read-only property.
person.name = "Juanito";

// Sin embargo, 'age' podemos reasignarlo sin problema ya que no es readonly:
person.age = 40;


// Ejemplo con interfaces
// ----------------------
interface IPerson {
  readonly name: string;
}

const person: IPerson = {
  name: "Pepito"
};

// Da el error: Cannot assign to 'name' because it is a read-only property.
person.name = "Juanito";


// Ejemplo con clases
// ------------------
class Car {
  private readonly WHEELS_NUMBER: number = 4;

  // Da el error: Cannot assign to 'WHEELS_NUMBER' because it is a read-only property.
  public set Wheels(newWheelsNumber: number) {
    this.WHEELS_NUMBER = newWheelsNumber;
  }
}

Eso sí, mucho cuidado en las clases, porque hay un caso que no falla. Y es la asignación dentro del constructor. Esto es debido a que en el constructor se permite inicializar todas las variables independientemente de que ya lo estuvieran:

// Ejemplo con clases
// ------------------
class Car {
  private readonly WHEELS_NUMBER: number = 4;

  public constructor() {
    // Esto no da error. Directamente omite la asignación de la línea 4.
    this.WHEELS_NUMBER = 2;
  }
}

// Si nos vamos al código compilado, lo que veremos es lo siguiente:
// class Car {
//   constructor() {
//     this.WHEELS_NUMBER = 4;
//     this.WHEELS_NUMBER = 2;
//   }
// }

En este caso te recomiendo que llegues a un consenso para o inicializar los atributos readonly de las clases nada más declararlos, o en el constructor, simplemente por claridad.

Método 3. Arrays constantes

Este es otro de los casos mágicos que nos permite TypeScript, y es tener arrays con elementos constantes. Es decir, que una vez creado el array, ya no podrá ser modificado. Para ello, los métodos push, pop, splice, etc, dejan de estar disponibles (es sólo a nivel de compilación de TypeScript, si accedes a ese array desde JavaScript será como cualquier otro).

const hobbies: readonly string[] = ["leer", "nadar", "series de ciencia ficción"];

// Da el error: Property 'push' does not exist on type 'readonly string[]'.
hobbies.push("dormir");

// Si intentamos, aunque sea con el anti-patrón de crear una nueva posición usando el índice directamente,
// también nos dará error. En este caso: Index signature in type 'readonly string[]' only permits reading.
hobbies[3] = "pasear";

Pues bien, en este artículo hemos visto 3 formas de poder tener elementos constantes en nuestro código TypeScript, espero que os haya gustado, y como siempre cualquier duda podéis dejarla en comentarios.

Si quieres un resumen puedes ver nuestro cheat sheet de atributos constantes en Instagram.

Comparte este artículo con quien quieras
11 cosas que quizás no sabías de JavaScript
Principios SOLID. Capítulo S: Single Responsibility Principle

Leave a Comment

Your email address will not be published. Required fields are marked *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.