Mostrando entradas con la etiqueta extends. Mostrar todas las entradas
Mostrando entradas con la etiqueta extends. Mostrar todas las entradas

miércoles, 25 de junio de 2025

Uso de "extends" y un tipo predeterminado en genéricos en Typescript

¿Qué es A en Typescript, en function f<T extends A = A>():B<T>?

En TypeScript, la sintaxis function f<T extends A = A>(): B<T> define una función genérica f con un parámetro de tipo T que tiene ciertas restricciones y valores predeterminados. Analicemos los componentes:


T extends A: Esto significa que el parámetro de tipo T debe ser un subtipo de A. En otras palabras, T solo puede ser un tipo asignable a A. Esta es una forma común de restringir los tipos genéricos para garantizar que tengan ciertas propiedades o métodos.

= A: Esto establece un tipo predeterminado para T si no se proporciona ningún tipo al llamar a la función. Si no se especifica ningún tipo, T tomará A como predeterminado.

(): B<T>: Esto indica que la función f devuelve un valor de tipo B<T>, donde B es otro tipo que depende de T.

En resumen, la función f es una función genérica que toma un parámetro de tipo T, que está restringido a ser un subtipo de A y toma A como predeterminado si no se especifica. La función devuelve un valor de tipo B<T>.

martes, 24 de junio de 2025

Validación de Typescript de subtipado: cuando una clase declara "extends" de otra, y cuando la extiende realmente.

Un argumento de tipo (es decir, tipo real) no declara "extends supertipo". Pero su parámetro de tipo correspondiente sí declara "extends supertipo". ¿Cómo permite Typescript esta relajación en la concordancia de declaración de supertipos?

Vamos a desglosarlo con un ejemplo para entenderlo mejor.


Entendiendo el funcionamiento en la garantía de concordancia de supertipos

Genéricos y Restricciones

Primero, recordemos qué son los genéricos y las restricciones:

  • Genéricos (<T>): Permiten escribir componentes o funciones que pueden trabajar con cualquier tipo de dato, manteniendo la flexibilidad y la seguridad de tipos. T es un marcador de posición para el tipo real que se usará.

  • Restricciones de Tipo (extends): A menudo, no quieres que tu genérico funcione con cualquier tipo, sino solo con tipos que tengan ciertas propiedades o que hereden de una clase específica. Usas extends para aplicar estas restricciones.

    Ejemplo de restricción:

    TypeScript
    interface HasId {
      id: string;
    }
    
    interface MyObject { // Observar que no declara "extends HasId"
      id: string;
      pepe: string;
    }
    
    // T debe ser un tipo que tenga una propiedad 'id' de tipo string
    function processItem<T extends HasId>(item: T) {
      console.log(`Processing item with ID: ${item.id}`);
    }
    

La concordancia de supertipos en Cuestión

Ocurriría un error si intentaras llamar a processItem con un tipo T que no tiene la propiedad id (o no es de tipo string), o si le falta alguna otra propiedad o método que HasId (el "supertipo" o la "restricción") espera.

Pero ¿Por qué el siguiente ejemplo no es un error?

myObject = new MyObject();
myObject.id = "123";
myObject.pepe = "abc";function processItem(myObject) { // MyObject es un buen sustituto de T porque tiene la propiedad 'id' de tipo string
  
console.log(`Processing item with ID: ${item.id}`); }

La clave está en que no hace falta que en la declaración de MyObject diga "extends HasId". En cambio, Typescript chequea miembro a miembro que MyObject, como cualquier sustituto de T, tenga las mismas propiedades con los mismos tipos que HasId.


En resumen, la validación de Typescript de subtipado funciona a partir de la concordancia de propiedades, funciones y sus tipos de datos, y no de la declaración "extends".