ProgramaciónDesarrollador Fullstack

¿Cómo funciona el operador keyof en TypeScript, para qué se utiliza y cómo influye en el diseño de funciones y API seguras?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la pregunta

TypeScript fue diseñado desde el principio para una tipificación estricta de objetos y para escribir código más predecible en JavaScript. Uno de los instrumentos para garantizar la seguridad al acceder a las propiedades de los objetos es el operador keyof, que apareció en TypeScript 2.1. Permite crear tipos que representan un conjunto de claves (cadenas y simbólicas) disponibles en el tipo de objeto declarado.

Problema

En JavaScript puro, las propiedades de un objeto pueden ser consultadas con cualquier cadena, y si hay un error, se obtendrá undefined o un error que solo se encontrará en tiempo de ejecución. Sin una tipificación estricta, es fácil cometer un error tipográfico o olvidar actualizar una cadena clave durante el refactorizado. Además, a menudo se requiere construir una función que solo acepte claves correctas de un objeto o tipo específico.

Solución

El operador keyof crea un tipo de unión de todas las claves del tipo. Con él, se pueden limitar las claves permitidas, aumentar la seguridad de la API (por ejemplo, para funciones getter/setter) y crear tipos utilitarios genéricos.

Ejemplo de código:

type User = { name: string; age: number; active: boolean }; type UserKey = keyof User; // "name" | "age" | "active" function getProp<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user: User = { name: 'Tom', age: 33, active: true }; const age = getProp(user, 'age'); // edad tipo number

Características clave:

  • Permite declarar tipos "clave" que dependen de la estructura del objeto.
  • Hace que las funciones de tipo get/set propiedades sean lo más seguras posible en términos de tipos: se excluyen errores tipográficos y claves inexistentes.
  • Funciona también con propiedades simbólicas (symbol), desde TypeScript 2.7 en adelante.

Preguntas engañosas.

¿Qué devuelve keyof para un arreglo y se pueden obtener los índices?

keyof para un arreglo devuelve los tipos de las claves del objeto, que en realidad es lo que el arreglo es en JS. Normalmente son cadenas de índices y propiedades especiales del arreglo.

Ejemplo de código:

type A = keyof number[]; // "length" | "toString" | "pop" | ... | number

¿Puede keyof devolver claves de tipos de unión?

Sí, keyof de un tipo de unión devuelve la intersección de las claves de ambos objetos, no su unión.

Ejemplo de código:

type A = {a: string, b: number } type B = {b: number, c: boolean } type C = keyof (A | B); // "b"

¿Qué ocurre si la propiedad de un objeto es opcional? ¿Admite keyof "?"?

Sí, las propiedades opcionales también están incluidas en el tipo resultante de claves, pero esto no significa que la propiedad deba estar presente en el objeto en tiempo de ejecución.

Ejemplo de código:

type D = { a: string; b?: number }; type DK = keyof D; // "a" | "b"

Errores de tipo y anti-patrones

  • Usar cadenas constantes para claves en lugar de keyof — pérdida de tipificación.
  • Suponer que keyof para unión devuelve una unión, en vez de una intersección.
  • Esperar que keyof considere solo las claves declaradas explícitamente, pero no las heredadas.

Ejemplo de la vida real

Caso negativo

Se escribe una función getProperty(obj, key) con una clave de tipo string, se llama con una clave inexistente — el error aparece solo en tiempo de ejecución.

Ventajas:

  • Código universal.

Desventajas:

  • No hay chequeo en tiempo de compilación, pueden haber errores durante el refactorizado, vulnerable a errores tipográficos.

Caso positivo

Se utilizan genéricos: getProperty<T, K extends keyof T>(), el tipo key está estrictamente limitado a las claves de la estructura.

Ventajas:

  • Seguridad absoluta en tipos, es imposible un error tipográfico o en la clave.

Desventajas:

  • El código es un poco más complicado, los genéricos no siempre son claros para los principiantes.