Ir al contenido principal

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".

Comentarios

Entradas populares de este blog

Crear proyecto de frontend con Vite, React, Tailwind, Typescript y JSX

 ¿Cómo se crea un proyecto exclusivo de frontend con Vite, React, Tailwind, Typescript y JSX? ¡Absolutamente! Entiendo que quieres la guía completa para pasar de cero a tener un proyecto de frontend moderno, vacío y listo para ser llenado con tu código. A continuación, te proporciono un archivo de guía detallada que incluye todos los comandos necesarios para inicializar el proyecto, configurar Tailwind CSS, y los pasos clave para empezar a "poblarlo". Voy a generar un documento Markdown con la guía paso a paso, incluyendo los comandos de terminal y las modificaciones esenciales a los archivos de configuración. Guía: Creación de un Proyecto Frontend Puro (Vite, React, TypeScript, Tailwind) Este documento detalla el proceso para inicializar un proyecto de frontend vacío y optimizado, utilizando Vite como empaquetador, React como librería, TypeScript para tipado y Tailwind CSS para estilizado. 1. Inicialización del Proyecto con Vite Vite es la herramienta más rápida para emp...

Tokens V2

Tokens: Programme to discover tokens, where there are not. Now available at  https://puszcza.gnu.org.ua/projects/tokens/ This is Version 2, for  version 1, go here . Synopsis: use TokensV2; sub printFile; my @FORMAT = ( ['<Message Date=".*?" Time=".*?" DateTime=".*?" SessionID=".*?"><From>(?:<User FriendlyName=".*?"/>)+</From><To>(?:<User FriendlyName=".*?"/>)+</To><Text(?: Style=".*?")?>.*?</Text></Message>',   sub {     my $fh = $_[1];     my ($d, $t, $f, $s, $T) = $_[0] =~ m|<Message Date="(.*?)" Time="(.*?)" DateTime=".*?" SessionID=".*?">(<From>(?:<User FriendlyName=".*?"/>)+</From>)<To>(?:<User FriendlyName=".*?"/>)+</To><Text(?: Style="(.*?)")?>(.*?)</Text></Message>|;     my $F = join '<br />', ...

Perl Net::LDAP::SimpleServer

Adaptaciones sobre el módulo LDAP Server para Windows (Strawberry Perl) Lista de adaptaciones (continúa más abajo): - Relajación de condiciones de bind:     - Cuenta principal (principal account)     - Validación de contraseñas Ubicación del archivo: %Strawberry_Perl%\site\lib\net\ldap\SimpleServer\ProtocolHandler.pm CPAN: http://search.cpan.org/~russoz/Net-LDAP-SimpleServer-0.0.17/lib/Net/LDAP/SimpleServer.pm Código: package Net::LDAP::SimpleServer::ProtocolHandler; use strict; use warnings; # ABSTRACT: LDAP protocol handler used with Net::LDAP::SimpleServer our $VERSION = '0.0.17';    # VERSION use Net::LDAP::Server; use base 'Net::LDAP::Server'; use fields qw(store root_dn root_pw allow_anon); use Carp; use Net::LDAP::LDIF; use Net::LDAP::Util qw{canonical_dn}; use Net::LDAP::FilterMatch; use Net::LDAP::Constant (     qw/LDAP_SUCCESS LDAP_AUTH_UNKNOWN LDAP_INVALID_CREDENTIALS/,   ...