Inicio / Artículos / Operadores de TypeScript en la práctica: Matemáticas, comparaciones, lógica y operadores ternarios

Operadores de TypeScript en la práctica: Matemáticas, comparaciones, lógica y operadores ternarios

Un recorrido práctico por los operadores aritméticos, de asignación, de comparación, lógicos, de incremento y ternarios de TypeScript, incluyendo la comprobación de igualdad que el compilador rechaza.

2635 palabras

Una vez que conozcas cómo TypeScript asigna y aplica tipos, el siguiente paso es hacer algo con los valores: sumarlos, compararlos, combinar condiciones y elegir entre alternativas. Los operadores son los símbolos que realizan esa tarea, y la mayoría de ellos se comportan exactamente como en JavaScript. Sin embargo, algunos ocultan detalles que suelen causar errores, como la igualdad laxa frente a la estricta y la diferencia entre x++ y ++x.

Esta guía aborda los operadores aritméticos, de asignación, relacionales, lógicos, de incremento y decremento, así como los operadores ternarios, con pequeños ejemplos sobre los que puedes reflexionar sin necesidad de ejecutarlos. A lo largo de ella verás dónde el compilador de TypeScript añade una red de seguridad que JavaScript puro no posee.

Un atajo primero: varias declaraciones en una sola sentencia

Antes de examinar los operadores, hay una conveniencia sintáctica en la que se basan los ejemplos posteriores. La forma habitual de declarar dos números es una instrucción por variable:

let a: number = 10;
let b: number = 20;

Un único let también puede introducir varias variables a la vez si se separan con comas:

let a: number = 10, b: number = 20;

Las dos formas se compilan de la misma manera; la segunda es simplemente más breve. Cada variable en la lista lleva su propia anotación, por lo que pueden tener tipos diferentes:

let a: number = 10, name: string = "John", isActive: boolean = true;

Use esta forma con moderación. Es legible para un par de valores estrechamente relacionados, como a y b utilizados en toda esta guía, pero una línea que mezcla un contador, un nombre y una bandera es más difícil de leer que tres declaraciones separadas.

Operadores aritméticos

Estos son los símbolos familiares de las matemáticas escolares. Los ejemplos a continuación comienzan con dos números:

// Arithmetic operators
let a: number = 10, b: number = 20;

Cada línea aplica un operador y muestra el resultado en un comentario:

console.log(a + b);   // 30   → addition
console.log(a - b);   // -10  → subtraction
console.log(a * b);   // 200  → multiplication
console.log(a / b);   // 0.5  → division
console.log(a % b);   // 10   → modulus (remainder after division)
console.log(a ** b);  // 100000000000000000000 → exponentiation (10 to the power 20)

Qué hace cada uno:

  • + suma los dos operandos.
  • - resta el operando de la derecha del de la izquierda.
  • * los multiplica.
  • / los divide sin redondeo. 10 / 20 realmente es 0.5; no existe una división entera separada como en algunos otros lenguajes, ya que todo número en JavaScript es un valor de punto flotante.
  • % devuelve el resto. 10 % 20 es 10: veinte entra en diez cero veces, por lo que todo el diez queda como resto.
  • ** eleva al poder el operando de la izquierda con respecto al de la derecha. 10 ** 20 es diez multiplicado por sí mismo veinte veces, lo que explica el resultado muy largo.

Una precaución con ** y resultados grandes: los números superiores a Number.MAX_SAFE_INTEGER (aproximadamente nueve cuatrillones) pierden precisión entera. 10 ** 20 se imprime exactamente, pero las operaciones aritméticas con valores de ese tamaño no son fiables; utilice bigint cuando necesite enteros grandes exactos.

Asignación y asignación compuesta

El simple = almacena un valor en una variable. JavaScript y TypeScript también ofrecen operadores de asignación compuesta que realizan un paso aritmético y almacenan el resultado en una sola expresión. Comience con dos valores:

let a: number = 100, b: number = 50;

Luego actualice a dos veces, una de la forma tradicional y otra con la abreviatura:

console.log(a);      // 100
a = a + b;
console.log(a);      // 150
console.log(a += b); // 200

Paso a paso: a comienza en 100. La instrucción a = a + b lee el valor actual de a, suma b (50) y escribe 150 de nuevo en a. No hay nada nuevo aquí aparte del operador + visto en la sección anterior.

La última línea utiliza a += b, que significa exactamente lo mismo que a = a + b, solo escrito de forma más compacta. Dado que a vale 150 en ese momento, su valor pasa a ser 200. Una asignación también es una expresión cuyo valor es el número recién almacenado, y por eso al pasar a += b directamente a console.log se imprime 200. Eso funciona, pero ocultar un cambio de estado dentro de un argumento de función hace que el código sea más difícil de seguir; por lo tanto, en el código real es mejor realizar la actualización en una línea separada.

El mismo patrón se aplica a cada operador aritmético: -=, *=, /=, %= y **= restan, multiplican, dividen, calculan el resto o elevan a una potencia, y luego asignan.

Operadores relacionales

Los operadores de comparación analizan dos valores y siempre producen un valor booleano, true o false. Estos son los operandos:

let a: number = 10;
let b: number = 20;

Y las comparaciones:

console.log(a > b);   // false
console.log(a < b);   // true
console.log(a === b); // false
console.log(a >= b);  // false
console.log(a <= b);  // true
console.log(a == b);  // false
console.log(a != b);  // true

Los operadores de orden son autoexplicativos: > (mayor que), < (menor que), >= (mayor o igual que) y <= (menor o igual que). Los operadores de igualdad ==, === y != comprueban si dos valores coinciden, pero == y === siguen reglas diferentes, y esa diferencia merece un apartado propio. El equivalente estricto de != es !==, y es el que normalmente se debe utilizar.

Igualdad laxa versus igualdad estricta

Los dos operadores de igualdad difieren en la forma en que tratan los tipos:

  • == es igualdad laxa. Si los operandos tienen tipos diferentes, convierte silenciosamente uno de ellos antes de comparar los valores.
  • === representa la igualdad estricta. Requiere el mismo tipo y el mismo valor, no realiza ninguna conversión y devuelve false siempre que los tipos difieran.
  • Para ilustrarlo, tomemos un número y una cadena que se parecen. Ambos están marcados deliberadamente como any; la razón se explica en breve.

    let num1: any = 10;
    let num2: any = "10";
    

    Compararlos con cada operador da respuestas diferentes:

    console.log(num1 == num2);  // true  → same value, type is ignored
    console.log(num1 === num2); // false → same value, but number ≠ string
    

    num1 almacena el número 10 y num2 almacena la cadena "10". La igualdad laxa convierte la cadena en un número, encuentra 10 en ambos lados y devuelve true. La igualdad estricta omite por completo la conversión; un número nunca es igual a una cadena, por lo que el resultado es false. Dado que las reglas de conversión de la igualdad laxa producen resultados sorprendentes (por ejemplo, 0 == "" es true), la mayoría de las guías de estilo y herramientas de análisis recomiendan usar === y !== en todas partes.

    Por qué el ejemplo necesita any

    Las anotaciones any no son algo casual. Si declara las variables con sus tipos reales, let num1: number = 10; y let num2: string = "10";, y luego escribe num1 === num2, el código no se compila. El compilador muestra un error más o menos de este tenor:

    This comparison appears to be unintentional because the types 'number' and 'string' have no overlap.
    

    TypeScript sabe que un valor de tipo number y un valor de tipo string nunca pueden ser iguales, por lo que considera esa comparación como un error. Esta verificación se aplica tanto a == como a ===. Al definir ambas variables como any se desactiva ese análisis de coincidencias, que es la única razón por la cual la demostración se compila. Es un ejemplo pequeño pero útil de cómo el sistema de tipos detecta comparaciones sin sentido antes de que ejecute el programa, algo sobre lo cual JavaScript puro nunca emite advertencias. También muestra por qué any es costoso: desactiva silenciosamente exactamente este tipo de protección. Para alternativas, consulte seis sustitutos seguros de tipo para any.

    Operadores lógicos

    Los operadores lógicos combinan condiciones. Hay tres: && (AND), || (OR) y ! (NOT). Comience con dos valores booleanos:

    let b1 = true;
    let b2 = false;
    

    Luego combínelos y negúelos:

    console.log(b1 && b2); // false
    console.log(b1 || b2); // true
    console.log(!b2);      // true
    console.log(!b1);      // false
    

    El comportamiento completo de AND y OR para cada par de entradas:

    • true y true: && da true, || da true.
    • true y false: && da false, || da true.
    • false y true: && da false, || da true.
    • false y false: && da false, || da false.

    Y para NO:

    • !true es false.
    • !false es true.

    En palabras:

    • && (Y) es estricto. Devuelve true solo si ambos lados son true; un false hace que toda la expresión sea false.
    • || (O) es indulgente. Basta con un solo lado true, y devuelve false solo cuando ambos lados son false.
    • ! (NO) toma un único operando y lo invierte: true se convierte en false y viceversa.

    Un detalle importante: ! siempre devuelve un valor booleano, pero && y || en realidad devuelven uno de sus operandos. Con entradas booleanas, esto siempre es un valor booleano, como se mencionó anteriormente. Con otros valores, "" || "default" da como resultado "default" y user && user.name devuelve ya sea user o user.name. Ambos operadores también funcionan por atajo: si el lado izquierdo ya determina el resultado, el lado derecho nunca se evalúa. Ese comportamiento es la razón por la cual a menudo se utilizan para valores predeterminados y condiciones de validación, y TypeScript asigna el tipo correspondiente al resultado.

    Combinar comparaciones con lógica

    Los operadores relacionales y lógicos suelen aparecer juntos: las comparaciones generan valores booleanos, y los operadores lógicos los combinan.

    console.log(20 > 10 && 20 < 10); // false
    console.log(20 > 10 || 20 < 10); // true
    

    En la primera línea, 20 > 10 es true y 20 < 10 es false. EL OPERADOR AND requiere que ambos sean true, por lo que el resultado es false.

    En la segunda línea las dos comparaciones son iguales, pero EL OPERADOR OR solo necesita un lado true, así que el resultado es true. Dado que los operadores de comparación se evalúan con mayor prioridad que && y ||, no se necesitan paréntesis aquí, aunque su uso puede hacer que las condiciones más largas sean más fáciles de leer.

    Aumento y disminución

    ++ suma 1 a un número y -- resta 1. Cada uno existe en dos formas, prefija y posfija, y la distinción es una fuente clásica de confusión. Comience con una sola variable:

    let x: number = 10;
    

    Cuando el operador se utiliza como instrucción por sí solo, ambas formas tienen el mismo efecto:

    x++; // same as x = x + 1
    console.log(x); // 11
    ++x; // same as x = x + 1
    console.log(x); // 12
    

    Escribidos en su propia línea, x++ y ++x simplemente suman uno a x. Las dos formas solo se comportan de manera diferente cuando el valor de la expresión se utiliza en la misma declaración, por ejemplo al asignarlo a otra variable. Ahí es donde el prefijo y el sufijo comienzan a ser importantes.

    Aumento sufijo: usar el valor y luego sumar uno

    "Sufijo" significa después: la expresión se evalúa con el valor actual de la variable, y el aumento ocurre posteriormente.

    let x: number = 10;
    let res: number = x++;
    

    Imprimir ambas variables muestra el efecto:

    console.log(res); // 10
    console.log(x);   // 11
    

    El orden de los eventos es:

    1. Se lee el valor actual de x, que es 10.
    2. Ese valor se convierte en el resultado de la expresión y se almacena en res.
    3. Luego x se incrementa a 11.

    Así, res mantiene el valor anterior, 10, mientras que x pasa a ser 11. El incremento sí ocurrió; simplemente sucedió después de que se copiara el valor.

    Incremento con prefijo: sumar uno y luego usar el valor

    "Pre" significa antes: la variable se incrementa primero, y la expresión devuelve el valor actualizado.

    let x: number = 10;
    let res: number = ++x;
    

    La salida lo confirma:

    console.log(res); // 11
    console.log(x);   // 11
    

    Aquí el orden es inverso:

    1. x pasa de 10 a 11.
    2. El nuevo valor, 11, se almacena en res.

    Ambas variables terminan siendo 11. En resumen: x++ devuelve el valor antes del incremento, mientras que ++x devuelve el valor después de él.

    El decremento sigue las mismas reglas

    -- es el inverso de ++, ya que resta en lugar de sumar. He aquí la forma con prefijo:

    let x: number = 10;
    let res: number = --x;
    

    Y su salida:

    console.log(res); // 9
    console.log(x);   // 9
    

    Con la disminución con prefijo, x pasa primero a 9 y ese nuevo valor se almacena en res, por lo que ambos imprimen 9. La forma con sufijo x-- haría lo opuesto: res recibiría el valor original 10, y solo entonces x bajaría a 9. Muchos equipos evitan por completo usar ++ y -- dentro de expresiones más complejas, precisamente porque la distinción entre prefijo y sufijo es fácil de malinterpretar; x += 1 por sí solo nunca es ambiguo.

    El operador condicional ternario

    El operador ternario expresa una simple elección if/else como una única expresión. Utiliza ? y :, y es el único operador de JavaScript que acepta tres operandos, de ahí su nombre. Comience con dos números:

    let a: number = 100;
    let b: number = 200;
    

    Luego elija el mayor de ellos:

    let res: number = (a > b) ? a : b;
    console.log(res); // 200
    

    La expresión tiene tres partes:

    1. (a > b) antes del ? es la condición.
    2. a, directamente después del ?, es el resultado cuando la condición es true.
    3. b, después del :, es el resultado cuando la condición es false.

    Al leerlo en voz alta, dice: si a es mayor que b, usar a; de lo contrario, usar b. Dado que 100 no es mayor que 200, la condición es false y res se convierte en 200.

    Sustituir un bloque if/else

    La principal ventaja es la brevedad. La misma lógica escrita como declaración ocupa varias líneas y requiere declarar res antes de asignarle un valor:

    // Without ternary
    let res: number;
    if (a > b) {
      res = a;
    } else {
      res = b;
    }
    

    La versión ternaria hace lo mismo en una sola línea. Estos dos fragmentos son alternativas, no código para pegar en el mismo ámbito, ya que declarar res dos veces con let generaría un error:

    // With ternary — same result, one line
    let res: number = (a > b) ? a : b;
    

    Dado que la expresión ternaria es también una expresión, permite declarar la variable e inicializarla en un solo paso, y TypeScript infiere o verifica el tipo del resultado de ambos ramas.

    Asociar un valor a una etiqueta

    Los operadores ternarios funcionan igualmente bien cuando las ramas tienen un tipo diferente al de la condición. Este ejemplo convierte una edad en una cadena descriptiva:

    let personage: number = 30;
    let res: string = (personage > 18) ? "adult" : "minor";
    console.log(res); // "adult"
    

    30 > 18 es true, por lo que res es "adulto". Con una edad de 15, la condición sería false y res sería "menor".

    Utilice los operadores ternarios para decisiones simples y únicas como estas. Una vez que comience a anidar un operador ternario dentro de otro para cubrir varios casos, la legibilidad disminuye rápidamente, y una cadena if...else if...else (o un objeto de consulta) suele ser más clara.

    Puntos clave

    • Los operadores de TypeScript son los mismos que los de JavaScript; el resultado compilado se comporta de manera idéntica en tiempo de ejecución.
  • / nunca trunca, % devuelve el resto, y los resultados muy grandes de ** pierden precisión a menos que se utilice bigint.
  • Las asignaciones compuestas como += son abreviaturas de “calcular y almacenar nuevamente”.
  • Preferir === y !==; la igualdad laxa convierte los tipos de maneras sorprendentes.
  • El compilador rechaza comparaciones entre tipos que nunca pueden superponerse, y any elimina silenciosamente esa protección.
  • && y || operan por cortocircuito y devuelven un operando, por lo que también sirven como protecciones y valores predeterminados.
  • x++ en posfijo devuelve el valor anterior, mientras que ++x en prefijo devuelve el nuevo; la diferencia solo se observa cuando se utiliza el resultado.
  • Utilice la estructura ternaria para una elección clara, y vuelva a usar if...else cuando las condiciones comiencen a acumularse.
  • Lecturas relacionadas