Genéricos con restricciones

Para facilitar el uso de una variable cuyo tipo usa genéricos en una función, lo normal es que pongamos restricciones, de tal manera que los genéricos que empleamos en tipos y funciones formen parte de una jerarquía de tipos que le asocie cierto contexto para hacerlos más fáciles de usar.

Cuando trabajamos con genéricos en TypeScript, una de las preguntas que surge rápidamente es cómo limitar los tipos que podemos pasar a esos genéricos para que cumplan ciertas condiciones o pertenezcan a una jerarquía concreta. Esto es especialmente útil cuando queremos que nuestro código sea más seguro y que el autocompletado nos ayude a escribirlo mejor.

Imaginemos que estamos diseñando una API para una red social y tenemos diferentes tipos de publicaciones: notas, notas coloridas, fotos y vídeos. En lugar de permitir que el genérico acepte cualquier tipo, podemos crear una jerarquía de interfaces para organizar estos tipos. Por ejemplo, definimos una interfaz común llamada Post y hacemos que todas las demás interfaces, como Nota, NotaColorida, Foto y Vídeo, extiendan de Post. Así, establecemos una relación clara entre ellas.

interface Post {
  id: number;
  fecha: Date;
}

interface Nota extends Post {
  contenido: string;
}

interface NotaColorida extends Nota {
  color: string;
}

interface Foto extends Post {
  url: string;
}

interface Vídeo extends Post {
  duracion: number;
}

Una vez que tenemos esta jerarquía, podemos restringir el tipo genérico para que solo acepte tipos que extiendan de Post. Esto se hace usando la palabra clave extends en la definición del genérico. Por ejemplo, si tenemos una función que recibe un parámetro genérico P, podemos escribirla así:

function publicar<P extends Post>(publicacion: P) {
  console.log(publicacion.id);
  // Aquí podemos acceder a las propiedades comunes de Post
}

Con esta restricción, TypeScript nos asegura que cualquier tipo que pasemos como argumento debe tener al menos las propiedades definidas en Post. Si intentamos pasar un número o cualquier otro tipo que no cumpla con esta interfaz, obtendremos un error de compilación.

Es importante destacar que la restricción no es una garantía absoluta en tiempo de ejecución, sino una verificación estática que hace el compilador. Por ejemplo, si Post solo tiene un método toString, cualquier tipo que tenga ese método podría ser considerado válido, incluso si no es lo que realmente queremos. Por eso, debemos diseñar bien nuestras interfaces para que reflejen correctamente las propiedades y métodos que queremos exigir.

Además, esta restricción mejora el autocompletado en los editores de código, ya que al saber que P extiende de Post, el editor puede sugerirnos las propiedades comunes que podemos usar sin miedo a errores.

En resumen, crear jerarquías de interfaces y usar extends para restringir tipos genéricos nos ayuda a escribir código más robusto y mantenible, evitando que se pasen tipos no deseados y facilitando el trabajo con las propiedades comunes de esos tipos.

Lista de reproducción
  1. 1
    Temporada 1
    5 minutos
  2. 2
    ¿Qué es TypeScript?
    11 minutos
  3. 3
    Instalando TypeScript
    8 minutos
  4. 4
    Compilando un Hola Mundo sencillo
    7 minutos
  5. 5
    Hola Mundo pero con tipos
    10 minutos
  6. 6
    Tipos: tipos primitivos
    12 minutos
  7. 7
    Tipos: tipos especiales (any, null, ...)
    10 minutos
  8. 8
    Tipos: arrays y tuplas
    11 minutos
  9. 9
    Tipos: objetos
    7 minutos
  10. 10
    Funciones: lo básico
    9 minutos
  11. 11
    Funciones: tipando funciones
    9 minutos
  12. 12
    Clases: introducción a las clases
    9 minutos
  13. 13
    Clases: creando una clase
    10 minutos
  14. 14
    Clases: modificador private
    8 minutos
  15. 15
    Clases: modificador readonly
    3 minutos
  16. 16
    Clases: Atributos virtuales con getters y setters
    10 minutos
  17. 17
    Clases: herencia
    9 minutos
  18. 18
    Clases: modificadores abstract y protected
    8 minutos
  19. 19
    Tipos alias
    6 minutos
  20. 20
    Tipos literales
    5 minutos
  21. 21
    Uniones de tipos
    7 minutos
  22. 22
    Uniones discriminantes
    7 minutos
  23. 23
    Intersecciones de tipos
    5 minutos
  24. 24
    Interfaces: introducción
    7 minutos
  25. 25
    Interfaces: modificadores y funciones
    9 minutos
  26. 26
    Interfaces: usándolas con clases
    8 minutos
  27. 27
    Interfaces: herencia de interfaces
    8 minutos
  28. 28
    Interfaces: interfaces indizadas
    5 minutos
  29. 29
    Interfaces: funciones y tipos híbridos
    5 minutos
  30. 30
    ¿Qué diferencia hay entre interfaces y tipos? (2020)
    8 minutos
  31. 31
    Casteos con as
    6 minutos
  32. 32
    instanceof y las guardas
    9 minutos
  33. 33
    Tipos enumerados
    8 minutos
  34. 34
    Valores avanzados para enumerados
    7 minutos
  35. 35
    Enumerados con valores computados
    6 minutos
  36. 36
    Genéricos en tipos
    8 minutos
  37. 37
    Múltiples genéricos y buenas prácticas
    5 minutos
  38. 38
    Genéricos en funciones
    8 minutos
  39. 39
    Genéricos con restricciones
    6 minutos
  40. 40
    Tipos de utilidad
    3 minutos
  41. 41
    Exportando módulos
    9 minutos
  42. 42
    Importando módulos
    7 minutos
  43. 43
    Export default e import asterisco
    6 minutos
  44. 44
    tsconfig
    7 minutos
  45. 45
    Módulos desde NPM
    7 minutos
  46. 46
    Arroba types y los .d.ts
    8 minutos
  47. 47
    Ejemplo (1): creando una API REST simple en TypeScript
    11 minutos
  48. 48
    Ejemplo (2): montando un servidor Express
    8 minutos
  49. 49
    Ejemplo (3): haciendo las funciones de control de datos
    11 minutos
  50. 50
    Ejemplo (4): conectando todas las piezas
    7 minutos