DanLevy.net

Quiz: Delicias dedesestructuración

¿Dominas la desestructuración?

¿O es tu Sinfonía de la Destrucción?

Este quiz evaluará tu dominio del Destructuring en JavaScript: desde la sintaxis “básica” de objetos hasta destructuring anidado y valores por defecto. ¡Además, incluye preguntas extra sobre TypeScript y tipos inline!

¡Vamos al calentamiento y demuestra tus habilidades de Destructuring! 👇

¿Qué imprimirá este código?

const person = {
name: 'Dan Levy',
location: 'Cape Town',
};
const { name, age } = person;
console.log(`Name: ${name}, Age: ${age}`);

La propiedad age no existe en person, así que age será undefined. Definitivamente no es Infinity 😅

Esto resulta en:

Name: Dan Levy, Age: undefined

¿Qué hará este código?

const person = [ 'Dan Levy', 'Cape Town' ];
const [ name, origin, age ] = person;
console.log(`Name: ${name}, Age: ${age}`);

La variable age no está presente en el arreglo person, así que será undefined.

Esto resulta en:

Name: Dan Levy, Age: undefined

¿Qué tal una desestructuración anidada?

'use strict';
const person = {
name: { first: 'Dan' },
address: { city: 'Denver' },
};
const {
name: { first },
address: { city },
birth: { place },
} = person;
console.log(
`First: ${first}, City: ${place}`,
);

La propiedad birth: { place } no existe en person, por lo que lanzará un error. Una solución es proporcionar valores predeterminados para las propiedades anidadas.

Al acceder a propiedades anidadas, ten cuidado, ya que los errores pueden ser difíciles de detectar. Además, los mensajes de error varían entre navegadores y otras plataformas, lo que complica un poco la depuración.

En Chrome moderno: TypeError: Cannot read properties of undefined (reading 'place')

En Node, también es un TypeError porque JavaScript intenta desestructurar place de undefined antes de que place se lea.

La redacción exacta varía entre navegadores y entornos de ejecución.

Ahora con algunos valores predeterminados, ¿qué hará esto?

'use strict';
const person = {
name: { first: 'Dan' },
address: { city: 'Denver' },
};
const {
name: { first = 'Unknown' },
birth: { place = 'Unknown' },
} = person;
console.log(
`Hi ${first} from ${place}`,
);

El valor predeterminado de place solo se puede aplicar después de obtener un objeto del que extraer esa propiedad. Aquí person.birth es undefined, por lo que la desestructuración falla antes de aplicar place = 'Unknown'. Falta el valor predeterminado del objeto: birth: { place = 'Unknown' } = {}.

TypeError

La redacción exacta del error varía entre navegadores y entornos de ejecución.

¿Qué hará esto?

const person = {
name: { first: 'Dan' },
address: { city: 'Denver' },
};
const {
name: { first = 'Unknown' },
birth: { place = 'Unknown' } = {},
} = person;
console.log(
`Hi ${first} from ${place}`,
);

La propiedad birth no existe en person, así que recurre a un objeto vacío = {}. Esto permite que se use el valor predeterminado.

Ahora como parámetros de función, ¿qué hará esto?

'use strict';
function displayUser({
name = "Unknown",
age = -1,
} = { place: "Unknown" }) {
console.log(`Hi ${name} from ${place}`);
}
displayUser({ name: "Dan" });

Esta función extrae las propiedades name y age, usando valores predeterminados si es necesario. En este caso, la clave place del objeto por defecto es solo ruido, no se usa dentro de displayUser().

El modo estricto no cambia esto: leer la variable no declarada place lanza un ReferenceError.

¿Cómo se manejan los valores undefined?

'use strict';
function displayPlace({
name = "N/A",
place = "N/A",
age = -1,
} = { place: "Unknown" }) {
console.log(`${place}`);
}
displayPlace({ name: "Dan" });
displayPlace({ name: "Dan", place: undefined });
displayPlace({ name: "Dan", place: "Joburg" });

El objeto predeterminado { place: "Unknown" } se usa si el argumento se omite o es explícitamente undefined: tanto displayPlace() como displayPlace(undefined) imprimen Unknown.

Si se proporciona un objeto, una propiedad place ausente o con valor undefined usa el valor predeterminado de la propiedad, "N/A". Los valores predeterminados del parámetro y de la propiedad se aplican a niveles distintos.

Esto produce:

displayPlace() // Unknown
displayPlace({ name: "Dan" }) // N/A
displayPlace({ name: "Dan", place: undefined }) // N/A

Similar al anterior… ¿cómo se maneja null?

function displayPlace({
name = "N/A",
place = "N/A",
age = -1,
} = { place: "Unknown" }) {
console.log(`${place}`);
}
displayPlace({ name: "Dan", place: null });
displayPlace({ name: "Dan", place: undefined });

En este caso, la propiedad place se establece en null en la primera llamada y en undefined en la segunda. El valor predeterminado "N/A" de la propiedad se usa si place falta o es undefined, incluso cuando se proporciona el objeto. null no activa ese valor predeterminado: las salidas son null y N/A.

A continuación, TypeScript

Ahora en TypeScript… ¿qué hará esto?

'use strict';
function displayPlace(
{
name = 'N/A',
place = 'N/A',
}: {
name: string;
place: string;
age: number;
},
) {
console.log(`${place}`);
}
displayPlace({ name: 'Dan', place: null });

TypeScript informa un error porque place está tipado como string, pero la llamada pasa null. Además, la llamada omite la propiedad requerida age.

Si ignoras los errores de tipo, ejecutar el código imprimirá null en la consola.

Probemos un poco de renombrado/asignación…

'use strict';
function displayPlace({
name = 'N/A',
place: location = 'N/A',
}: {
name: string;
place: string;
age?: number;
}) {
console.log(`${location}`);
}
displayPlace({ name: 'Dan', place: 'Denver' });

Esto imprimirá Denver en la consola. La propiedad place se renombra a location en la firma de la función. Es un patrón común (renombrar propiedades durante la desestructuración) al adaptar estructuras de datos de terceros.

Detecta el error de tipo:

function greet({
name: {first = "N/A", last = "N/A"},
birth: {place = "N/A"} = {},
age = -1,
}: {
name: {first?: string, last?: string};
birth: {place?: string};
age: number;
}) {
console.log(`Hi ${first} ${last} from ${place}`);
}
greet({ name: {first: 'Dan'} });

El error está en la firma de la función greet. Las propiedades age y birth faltan en el objeto pasado, por lo que deberían ser opcionales en la definición del tipo.

Aunque la propiedad birth se desestructura con un valor predeterminado, la definición del tipo requiere que esté presente. Para marcar una propiedad como opcional en TypeScript, debes usar el operador ?.

Nota que birth?: { place?: string } no es lo mismo que birth: { place?: string } | undefined.

Ahora con asignación (nota las variables f, l y p)

'use strict';
function greet(
{
name: {first: f = "N/A", last: l = "N/A"},
birth: {place: p = "N/A"} = {},
age = -1,
}: {
name: {first?: string, last?: string};
birth?: {place?: string};
age?: number;
}
) {
console.log(`Hi ${f} ${l} from ${place}`);
// What will 👆 do?
}
greet({
name: {first: 'Dan', last: 'Levy'},
birth: {place: 'Cape Town'},
});

¡Otro error! ¿Estás empezando a adivinar, ¿no?!

Es difícil leer capas de desestructuración, con valores predeterminados, asignación y tipos!

En cuanto place se reasigna a la variable p, ya no está definido en el alcance de la sentencia console.log.

console.log(`Hi ${f} ${l} from ${place}`); // ❌
// to:
console.log(`Hi ${f} ${l} from ${p}`); // ✅

¿Quieres seguir practicando? Visita la colección de cuestionarios.