Operador de encadenamiento opcional
Clase 4 de 47 min de lectura
Intenta leer una propiedad de algo que no existe y… se rompe tu aplicación. Es uno de los tropiezos más habituales en JavaScript, y tiene arreglo.
Hay varias formas de evitarlo. Las repasamos de la más aparatosa a la más elegante, hasta llegar al operador de encadenamiento opcional, también llamado Optional Chaining.
Te va a salvar sobre todo cuando trabajes con objetos de estructura compleja, o con datos que pueden no estar definidos en algún momento de la ejecución.
El error fatal de intentar leer una propiedad de undefined#
Uno de los errores más comunes en JavaScript, y te aseguro que lo verás tarde o temprano, es el que dice que no puedes leer una propiedad de undefined.
Aparece cuando intentas acceder a una propiedad de algo que no existe. Mira la última línea:
const gamesystem = { name: 'PS5', price: 550, specs: { cpu: 'AMD Ryzen Zen 2', gpu: 'AMD Radeon RDNA 2', }}console.log(gamesystem.name) // -> PS5console.log(gamesystem.specifications) // -> undefinedconsole.log(gamesystem.specifications.ram) // ❌ Uncaught TypeError: Cannot read properties of undefined (reading 'ram')Parece un despiste inocente: hemos escrito specifications en lugar de specs. Leer una propiedad que no existe no es grave, devuelve undefined. El problema llega justo después, cuando le pedimos ram a ese undefined. Ahí JavaScript dice basta.
Evitando el error con if
La primera solución que se te ocurriría: comprobar con un if que la propiedad existe antes de seguir.
if (typeof gamesystem.specifications === 'object') { console.log(gamesystem.specifications.ram)} else { console.log('No hay especificaciones')}Usamos el operador typeof, que ya vimos en la primera sección del curso, para asegurarnos de que specifications existe y es un objeto antes de leer su propiedad ram.
El operador in para comprobar si una propiedad existe#
Otra opción es el operador in. Le preguntas si una propiedad existe en un objeto y te contesta true o false:
console.log('name' in gamesystem) // -> trueconsole.log('specifications' in gamesystem) // -> falseconsole.log('specs' in gamesystem) // -> trueif ('specifications' in gamesystem) { console.log(gamesystem.specifications.ram)} else { console.log('No hay especificaciones')}Tiene buena pinta. Pero ¿qué pasa si la propiedad existe y su valor es undefined?
const gamesystem = { name: 'PS5', price: 550, specifications: undefined,}console.log('specifications' in gamesystem) // -> truegamesystem.specifications.ram // ❌ Uncaught TypeError: Cannot read properties of undefined (reading 'ram')Que volvemos a la casilla de salida. El operador in te dice si la propiedad existe, pero no si su valor es undefined o null. Para ir sobre seguro tendrías que añadir más comprobaciones:
if ( 'specifications' in gamesystem && gamesystem.specifications !== undefined && gamesystem.specifications !== null) { console.log(gamesystem.specifications.ram)} else { console.log('No hay especificaciones')}Mucho código para algo tan simple. Por suerte, hay una forma más corta.
El operador de Encadenamiento Opcional, ?.#
El operador de encadenamiento opcional ?. te deja leer una propiedad que está en lo más profundo de una cadena de objetos sin validar a mano cada eslabón. Es como el punto de siempre, pero prudente: antes de seguir, mira si hay algo a su izquierda.
const gamesystem = { name: 'PS5', price: 550, specs: { cpu: 'AMD Ryzen Zen 2', gpu: 'AMD Radeon RDNA 2', }}console.log(gamesystem.specifications?.cpu)// -> undefinedconsole.log(gamesystem.specs?.cpu)// -> AMD Ryzen Zen 2Como specifications no existe, ?. se detiene, devuelve undefined y no hay ningún error. Como specs sí existe, ?. se comporta como un punto normal y devuelve el valor de cpu.
?. se comporta como el punto: gamesystem.specs?.cpu devuelve el valor.Beneficios del Operador de Encadenamiento Opcional
El gran beneficio es que el código queda más corto y más legible. En vez de encadenar comprobaciones para asegurarte de que existen el objeto y cada una de sus propiedades, vas directo a la que te interesa. Si algo falta por el camino, JavaScript no se queja.
Lo vas a agradecer especialmente con datos dinámicos, donde no siempre puedes garantizar la estructura exacta de los objetos. Compara las dos formas de leer el mismo dato:
const user = { name: 'Peter', settings: { theme: 'dark', notifications: { email: true, push: false, marketing: undefined } }}// la forma clásica de acceder a una propiedad anidada// de forma seguralet email = undefinedif (user && user.settings && user.settings.notifications && user.settings.notifications.email) { email = user.settings.notifications.email}console.log(email) // -> true// con Optional Chaining Operatorconst safeEmail = user?.settings?.notifications?.emailconsole.log(safeEmail) // -> trueSeis líneas contra una, y el mismo resultado. Cuando no puedes garantizar la estructura o la presencia de ciertos datos, el encadenamiento opcional te ayuda a escribir un código más limpio, legible y resistente a errores.
En resumen
- Leer una propiedad de
undefinedonulllanza unTypeErrory detiene tu código. - Comprobarlo con
if,typeofo el operadorinfunciona, pero obliga a encadenar muchas comprobaciones. objeto.propiedad?.otradevuelveundefinedsi lo que hay antes de?.esundefinedonull, sin lanzar error.- Puedes encadenar varios
?.para leer datos muy anidados de forma segura.