En Typescript, ¿cómo puedo restringir el tipo de un campo en un tipo genérico a un tipo particular? En otras palabras, quiero escribir una función que acepte un objeto obj y una clave key de ese objeto, pero genere un error de mecanografiado si obj[key] no es del tipo X Por ejemplo, si X es string :
function foo<T extends {}, K extends keyof T ???>(obj: T, key: K) { console.log(obj[key]) } >> foo({ fox: 'Mulder' }, 'fox') Mulder >> foo({ fox: 22 }, 'fox') <some error here>La respuesta de Guerric demuestra cómo los parámetros de tipo generalmente están restringidos: 1) define T restringido para ser un objeto, luego 2) define K restringido para ser una clave de T .
Sin embargo, para el caso del OP, invertiría esta lógica: 1) definir la Key restringida para que sea una clave de propiedad genérica (también conocida como string | number | symbol ), luego 2) definir Obj restringida para que sea un objeto que contenga la Key de propiedad:
function foo<Key extends PropertyKey, Obj extends Record<Key, string>>(obj: Obj, key: Key) { console.log(obj[key]); } foo({ fox: 'Mulder' }, 'fox'); // works fine foo({ fox: 22 }, 'fox'); // Error (number is not assignable to string) De esta manera, obtendrá errores legibles (en este contexto, "el número no se puede asignar a la cadena" es mucho menos confuso que "la cadena no se puede asignar a nunca"), y también la implementación de la función ( console.log(obj[key]) bit) no requiere ningún encasillamiento ya que obj se escribe correctamente para tener una Key clave.
Podrías hacer esto:
function foo<T extends {}, K extends keyof T>(obj: T, key: T[K] extends string ? T[K] : never) { console.log((obj as any)[key]); } foo({ fox: 'Mulder' }, 'fox'); // OK foo({ fox: 22 }, 'fox'); // Argument of type 'string' is not assignable to parameter of type 'never' Implica convertir obj as any en la implementación porque never no se puede usar como un tipo de índice, aunque nada se puede asignar a never , por lo que una llamada de función con un parámetro de key real que haría que el tipo de key se resuelva en never nunca se compilará ( por definición), pero TypeScript no es lo suficientemente inteligente como para inferir eso.