Protecciones de tipos de TypeScript

Los protectores de tipo son una característica poderosa de TypeScript que permite a los desarrolladores realizar verificaciones en tiempo de ejecución para limitar el tipo de una variable. Esto garantiza información de tipo más precisa, lo que genera un código más seguro y predecible. Este artículo explora qué son los protectores de tipo y cómo usarlos de manera efectiva.

¿Qué son los protectores de tipo?

Los protectores de tipo son expresiones que realizan comprobaciones en tiempo de ejecución y permiten que TypeScript infiera un tipo más específico para una variable. Ayudan a discriminar entre diferentes tipos, especialmente cuando se trata de tipos de unión. Los protectores de tipo se pueden implementar utilizando varias técnicas, entre ellas:

  • Predicados de tipo definidos por el usuario
  • Afirmaciones de tipo
  • Comprobaciones de instancias
  • Uso del operador typeof
  • Uso del operador in

Predicados de tipo definidos por el usuario

Los predicados de tipo definidos por el usuario son funciones que devuelven un valor booleano y tienen un tipo de retorno especial que indica el tipo de la variable que se está verificando. A continuación, se explica cómo crearlos y utilizarlos:

function isString(value: any): value is string {
  return typeof value === 'string';
}

function printString(value: any) {
  if (isString(value)) {
    console.log(value.toUpperCase()); // TypeScript knows value is a string here
  } else {
    console.log('Not a string');
  }
}

En el ejemplo anterior, isString es un predicado de tipo definido por el usuario que ayuda a TypeScript a comprender que value es una cadena dentro del bloque if.

Afirmaciones de tipo

Las afirmaciones de tipo le indican a TypeScript que trate una variable como un tipo determinado. Este método no realiza una comprobación en tiempo de ejecución, sino que informa al compilador de TypeScript sobre el tipo. Por ejemplo:

function printLength(value: any) {
  console.log((value as string).length); // Assert value is a string
}

En este ejemplo, value as string le indica a TypeScript que asuma que value es una cadena sin realizar una verificación en tiempo de ejecución.

Comprobaciones de instancias

Las comprobaciones de instancia se utilizan para determinar si un objeto es una instancia de una clase en particular. Esto resulta útil para limitar los tipos cuando se trabaja con clases:

class Dog {
  bark() { console.log('Woof'); }
}

class Cat {
  meow() { console.log('Meow'); }
}

function speak(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark(); // TypeScript knows animal is a Dog here
  } else {
    animal.meow(); // TypeScript knows animal is a Cat here
  }
}

En este ejemplo, el operador instanceof ayuda a TypeScript a inferir el tipo de animal en función de su clase.

Uso del operador typeof

El operador typeof se puede utilizar para comprobar tipos primitivos como string, number y boolean:

function processValue(value: string | number) {
  if (typeof value === 'string') {
    console.log(value.toUpperCase()); // TypeScript knows value is a string here
  } else {
    console.log(value.toFixed(2)); // TypeScript knows value is a number here
  }
}

Aquí, typeof se utiliza para verificar si value es una string o un number y limita el tipo en consecuencia.

Uso del operador in

El operador in comprueba la presencia de una propiedad en un objeto. Esto resulta útil para distinguir entre tipos que comparten propiedades comunes:

interface Bird {
  fly: () => void;
}

interface Fish {
  swim: () => void;
}

function move(creature: Bird | Fish) {
  if ('fly' in creature) {
    creature.fly(); // TypeScript knows creature is a Bird here
  } else {
    creature.swim(); // TypeScript knows creature is a Fish here
  }
}

En este ejemplo, el operador in ayuda a TypeScript a determinar si creature es un Bird o un Fish según la presencia de un método.

Conclusión

Los protectores de tipos de TypeScript son herramientas esenciales para trabajar con tipos de forma flexible y segura. Permiten una comprobación de tipos más precisa y pueden evitar errores de ejecución al garantizar que se utilice el tipo correcto en diferentes situaciones. Comprender y utilizar los protectores de tipos de forma eficaz puede dar lugar a un código TypeScript más sólido y fácil de mantener.