Saltar al contenido principal

Un caso curioso de los ternarios

· 6 min de lectura
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

El formato de los ternarios siempre ha sido un desafío, y finalmente lo abordamos en la versión 3.1.0 con la introducción de un estilo de formato novedoso.

Sigue leyendo para conocer nuestro recorrido y la motivación detrás de este cambio, junto con los comentarios iniciales de los desarrolladores y una descripción general del estilo de "ternarios curiosos".

¡Prueba la opción --experimental-ternaries y cuéntanos qué opinas!

Para un resumen rápido (tl;dr), consulta la publicación de lanzamiento.

Introducción

Dar formato a ternarios anidados de manera adecuada en una amplia variedad de escenarios es un desafío sorprendentemente complejo.

Los desarrolladores los han considerado tan confusos de leer que terminan refactorizando su código a una serie poco elegante de declaraciones if-else, a menudo con una declaración let, una IIFE o una función separada por completo.

Según los beta testers, el nuevo estilo de formato que hemos desarrollado puede requerir cierto tiempo de adaptación, pero finalmente permite usar los ternarios prácticamente como una forma concisa de expresiones if-else en bases de código modernas.

Antecedentes históricos

El enfoque original e ingenuo de Prettier - simplemente agregar sangría a cada nivel de un ternario anidado - funcionaba bien en casos simples, pero obviamente no escala a cadenas largas de ternarios anidados y tenía otros problemas.

Así que en 2018, lo reemplazamos con ternarios planos, lo cual parecía una buena idea en ese momento, pero no fue bien recibido - la incidencia que pedía revertirlo superó ampliamente los 500 votos.

Aunque finalmente revertimos a los ternarios con sangría, queríamos encontrar una mejor solución.

Durante los últimos años, exploramos y experimentamos con muchas soluciones posibles que fueran tan legibles como los ternarios con sangría en casos comunes, pero que también escalaran bien en una mayor variedad de situaciones.

Criterios desafiantes

Idealmente, encontraríamos un esquema que cumpliera con nuestros criterios:

  1. En todos los casos, debería ser fácil seguir "cuál es el if", "cuál es el then" y "cuál es el else" - y a qué se corresponden.

  2. El código debería fluir naturalmente desde un solo ternario, a una cadena de 2, a una cadena larga de casos simples, hasta algo más complejo con algunas condiciones anidadas. (La mayoría de alternativas que exploramos fallaron esta prueba).

  3. La sintaxis en JSX, expresiones condicionales de TypeScript (que no pueden expresarse con if) y JS normal deberían verse y sentirse iguales.

  4. Debería escalar a cadenas de ternarios anidados de longitud arbitraria (imagina un tipo condicional de TypeScript con docenas de casos alternativos).

Los ternarios con sangría claramente fallaron en (4), discutiblemente en (1), e incluso en (3) - casi siempre hemos impreso ternarios en JSX en un formato plano pero legible que desafortunadamente se sentía antinatural fuera de JSX.

Muchos en la comunidad se entusiasmaron con un "estilo de casos", inspirándose en la sintaxis match de lenguajes como Rust u OCaml, pero no cumplía con el criterio (2) y otros objetivos.

Una solución sorprendente

La buena noticia es que encontramos un algoritmo de formato que cumple nuestros criterios. La mala noticia es que es novedoso y, por tanto, desconocido para la mayoría de desarrolladores.

En las pruebas beta, observamos que los desarrolladores eran bastante escépticos al verlo por primera vez:

"No estoy convencido de que la nueva versión sea más fácil de leer aquí."

Pero tras usarlo un tiempo, ya no querían volver atrás:

"¡Me gustan los ternarios! Creo que tiene sentido formatearlos así. Además me acostumbré bastante rápido.\nEstoy de acuerdo, toma muy poco tiempo habituarse."

Otro desarrollador comentó:

En mi primera hora con la regla activada, se sentía un poco raro. Pero para la segunda hora, ya lo había usado varias veces para resolver problemas que de otro modo habrían requerido refactorizaciones feas con sentencias if. No pienso volver atrás.

Solía odiar los ternarios anidados, pero también detesto transformar una línea de código limpia en declaraciones if-else. La nueva regla añade una expresión lineal comprensible de if-else, if-else al lenguaje y es mucho mejor que múltiples ternarios como ramas anidadas.

Así que consideramos que teníamos una fórmula ganadora, aunque sabíamos que podría ser impactante para la comunidad.

Por ello, decidimos ocultar este nuevo formato tras una opción temporal --experimental-ternaries durante unos meses, y mientras tanto lanzar lo que la comunidad ha estado pidiendo: ternarios con sangría.

Resumen del estilo

Entonces, ¿cómo es exactamente este nuevo estilo?

Un ejemplo rápido y artificial para mostrar la lógica de los ternarios "curiosos":

const animalName =
pet.canBark() ?
pet.isScary() ?
'wolf'
: 'dog'
: pet.canMeow() ? 'cat'
: 'probably a bunny';
  1. Cada línea que termina con signo de interrogación es un "if".

    • Si ves foo ? es como plantear una pregunta sobre foo – "si foo? entonces, …".
  2. Cada línea que comienza con : es un "else".

    • Si ves : foo significa "sino, foo".
    • Si ves : foo ? significa "sino, si foo?".
  3. Cada línea sin : ni ? es un "then".

    • Si solo ves foo, significa "entonces foo".

Y aquí el código reescrito para demostrar ternarios de "estilo caso":

const animalName =
pet.isScary() ? 'wolf'
: pet.canBark() ? 'dog'
: pet.canMeow() ? 'cat'
: 'probably a bunny';

Puede verse como una forma concisa de obtener algo similar a la sintaxis match en JavaScript, usando solo el humilde operador ternario (aunque le faltan varias características).

Nuestro nuevo formato es una mezcla fluida de ternarios "curiosos" (donde el signo de interrogación siempre está al final de la línea) y ternarios de "estilo caso", donde el signo de interrogación está en medio de la línea.

Por ejemplo:

const animalName =
pet.canSqueak() ? 'mouse'
: pet.canBark() ?
pet.isScary() ?
'wolf'
: 'dog'
: pet.canMeow() ? 'cat'
: pet.canSqueak() ? 'mouse'
: 'probably a bunny';

¡Déjanos tu feedback!

Esperamos que te guste la nueva opción predeterminada más legible, y realmente esperamos que pruebes la nueva opción --experimental-ternaries durante algunas semanas y nos cuentes qué te parece.

Por favor, danos tu opinión a través de Google Forms: https://forms.gle/vwEuboCobTVhEkt66