Mutable e Inmutable en TypeScript

Mutable e Inmutable en TypeScript

Este artículo explica los conceptos de mutable e inmutable en TypeScript.

YouTube Video

Mutable e Inmutable en TypeScript

¿Qué es Mutable?

Mutable significa que un valor puede ser cambiado. Los tipos referencia como objetos y arrays son ejemplos típicos de estructuras de datos mutables.

Ejemplo de un Objeto Mutable

1type Person = { name: string; age: number };
2
3// Mutable Example: Object
4let person: Person = { name: "Alice", age: 25 };
5person.age = 26;
6console.log(person); // { name: "Alice", age: 26 }

En este código, la propiedad age del objeto person se cambia de 25 a 26. Dado que los objetos se pasan por referencia, se modifican los contenidos en la dirección de memoria almacenada en la variable person.

Ejemplo de un Array Mutable

1// Mutable Example: Array
2let numbers: number[] = [1, 2, 3];
3numbers.push(4);
4console.log(numbers); // [1, 2, 3, 4]

En este código, se usa el método push para agregar un nuevo elemento 4 al array original. Esto modifica el array original, convirtiéndolo en una operación mutable.

Ejemplo en una Función

1// Mutable Example: Function
2function append(arr: number[], value: number): void {
3    arr.push(value); // Modify the original array
4    console.log(arr);
5}
6
7let nums: number[] = [1, 2, 3];
8append(nums, 4);
9console.log(nums); // [1, 2, 3, 4]

Al realizar operaciones mutables dentro de una función, el array original también se modifica.

¿Qué es Inmutable?

Inmutable significa que un valor no puede ser cambiado. Los tipos primitivos son fundamentalmente inmutables.

Ejemplo de un Tipo Primitivo Inmutable

1// Immutable Example: String
2let str: string = "hello";
3str[0] = "H"; // Error: Index assignment is not allowed
4console.log(str); // "hello"

Intentar cambiar el primer carácter de la cadena str a H falla porque las cadenas son inmutables.

Ejemplo en una Función

1// Immutable Example: Function
2function increment(num: number): number {
3    num++; // This modifies only the local copy of num
4    return num;
5}
6
7let number: number = 10;
8console.log(increment(number)); // 11
9console.log(number); // 10 (original number remains unchanged)

Dado que los números son inmutables, las operaciones dentro de una función no afectan la variable original.

Operaciones Inmutables en Arrays

Los arrays son mutables, pero al crear un nuevo array en lugar de modificar el original, se pueden lograr operaciones inmutables.

1// Create an array of numbers
2let numbers: number[] = [1, 2, 3];
3
4// Immutable Example: Creating a new array
5let newNumbers: number[] = [...numbers, 4];
6
7console.log(numbers); // [1, 2, 3] (original array is unchanged)
8console.log(newNumbers); // [1, 2, 3, 4] (new array with an added element)

Aquí, la sintaxis de propagación (...) se utiliza para crear un nuevo array newNumbers. Dado que el array numbers original permanece sin cambios, esta es una operación inmutable.

Beneficios de Usar Estructuras de Datos Inmutables

Mejor Predictibilidad

Los datos inmutables no cambian, lo que hace menos probables las modificaciones inesperadas y reduce la posibilidad de errores.

Compatibilidad con Bibliotecas Basadas en Inmutabilidad

Bibliotecas como React y Redux suelen estar diseñadas sobre la base de datos inmutables, lo que facilita la gestión de estados cuando se usan adecuadamente.

Haciendo Objetos Inmutables con Object.freeze

Object.freeze se puede utilizar para evitar modificaciones en un objeto.

1// Create a frozen object (properties cannot be modified)
2const person = Object.freeze({ name: "Alice", age: 25 });
3
4// Attempt to modify a property (ignored in non-strict mode, error in strict mode)
5person.age = 26;
6
7console.log(person); // { name: "Alice", age: 25 }

Sin embargo, Object.freeze solo realiza un congelamiento superficial, lo que significa que las propiedades de objetos anidados siguen siendo mutables.

1// Create a frozen object with a nested object
2const user: Readonly<{ profile: { name: string } }> = Object.freeze({
3    profile: { name: "Bob" }
4});
5
6// Attempt to modify a nested property (this works because Object.freeze() is shallow)
7user.profile.name = "Charlie"; // No TypeScript error, but still mutable
8
9console.log(user.profile.name); // "Charlie" (nested object is still mutable)

Para crear un objeto completamente inmutable, se requiere un congelamiento profundo.

 1// Function to deeply freeze an object, making all nested objects immutable
 2function deepFreeze<T>(obj: T): Readonly<T> {
 3    Object.freeze(obj);
 4    Object.getOwnPropertyNames(obj).forEach(prop => {
 5        const value = (obj as any)[prop];
 6        if (typeof value === "object" && value !== null) {
 7            deepFreeze(value);
 8        }
 9    });
10    return obj;
11}
12
13// Create a deeply frozen object
14const user = deepFreeze({
15  profile: { name: "Bob" }
16});
17
18// Attempt to modify a nested property
19// (this will now throw an error in strict mode)
20user.profile.name = "Charlie";  // No TypeScript error, but modification is not allowed at runtime
21
22console.log(user.profile.name); // "Bob" (unchanged due to deep freeze)

Resumen

  • Los datos mutables son modificables e incluyen objetos y arrays.
  • Los datos inmutables no se pueden modificar e incluyen tipos primitivos como cadenas y números.
  • Usando la sintaxis de propagación o métodos como map, se pueden realizar operaciones con datos inmutables.
  • Object.freeze y deepFreeze se pueden usar para evitar modificaciones en objetos.
  • Usar datos inmutables ayuda a escribir código más predecible y menos propenso a errores.

Adoptar un diseño inmutable mejora la seguridad y legibilidad del código, ¡así que haz pleno uso de ello!

Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.

YouTube Video