Los genéricos son un concepto algo avanzado de Typescript y que a mucha gente le cuesta aprender (yo me incluyo ya que al principio me costó aprenderlo), pero ya verás que no es tan complicado.

Los genéricos permiten crear tipos que son algo más flexibles que los tipos normales de Typescript, ya que permiten dejar un "hueco" en el que pasar un tipo desde fuera.

Pongamos que tenemos Interfaces en Typescript para modelar coches, y dentro tiene un campo purchaseDate para indicar la fecha de compra y otro campo manufacturingDate para la de fabricación. A priori no sabemos si esa fecha va a ser un string o un campo de tipo Date de Javascript.

De primeras puedes pensar que esto se resuelve metiendo unión de tipos para que pueda ser de ambos tipos:

interface Car {
  model: string,
  purchaseDate: string | Date,
  manufacturingDate: string | Date,
}

Y sí, se podría resolver así, pero que pasa si queremos que ambas fechas tengan el mismo tipo, es decir, que si establecemos purchaseDate como string entonces que la de manufacturingDate también lo sea.

Pues la forma sería usando los genéricos, ya que podríamos indicar a la interfaz que queremos usar el tipo string, por ejemplo. Para crear los genéricos tienes que usar la sintaxis de menor qué y mayor qué y dentro poner el nombre para referirnos a ese tipo dentro de la interfaz, con un ejemplo se ve mejor:

interface Car<DateType> {
  model: string,
  purchaseDate: DateType,
  manufacturingDate: DateType,
}

Ahora no puedes usar esta interfaz como hasta ahora, tienes que pasar el tipo que quieres usar para las fechas, ejemplo:

const toyota: Car<string> = {
  model: "Toyota",
  purchaseDate: "2022-07-13",
  manufacturingDate: "2022-06-10",
}

const ford: Car<Date> = {
  model: "Ford",
  purchaseDate: new Date("2022-03-25"),
  manufacturingDate: new Date("2022-03-02"),
}

Como ves he creado dos objetos usando la interfaz, y en cuanto defino el tipo que quiero usar para la fecha, ya me obliga a que ambas sean del mismo tipo, en un caso string y en el otro tipo Date de Javascript.

En Internet verás que mucha gente crea los genéricos usando T como nombre. Esto es una mala idea porque al usarlo no vas a saber para qué es el tipo que tienes que pasar desde fuera.

interface Car<T> {
  model: string,
  purchaseDate: T,
  manufacturingDate: T,
}

También se pueden declarar varios genéricos en un tipo, simplemente pones los nombres separados por comas.

interface Car<DateType, ExtrasType> {
  model: string,
  purchaseDate: DateType,
  manufacturingDate: DateType,
  extras: ExtrasType
}

Esto de los genéricos también se puede usar dentro del Tipado de funciones en Typescript. Por ejemplo imaginemos que tenemos una función que recibe una fecha y devuelve la fecha del día anterior a la que pasamos.

En este caso queremos que la fecha que enviamos tenga el mismo tipo que la que recibimos, pudiendo ser de varios tipos, caso perfecto para los genéricos:

function getPreviousDayFromDate<DateSyntax>(date: DateSyntax): DateSyntax {
  ...
}