Piense en la siguiente línea de código en Typescript:
let x: 'a' | 'b' extends 'a' ? true : false; Me preguntaba si el tipo de x sería verdadero ya que intuitivamente 'a' | 'b' es una versión extendida de 'a' (al menos mi intuición lo dice). Pensé que las extends funcionarían como subconjuntos en matemáticas. A extends B a B si y si B ⊆ A .
Sin embargo, parece que el tipo real x es false aquí. Supongo que no entiendo cómo funciona exactamente la palabra clave extends .
Según el principio de sustitución de Liskov , si "Y extiende X" o, de manera equivalente, "Y es un subtipo de X", entonces se puede usar un valor conforme al tipo Y siempre que se solicite un valor conforme al tipo X. Esto lleva a una conclusión contraria a la intuición: al referirse al universo de valores posibles, si Y extiende a X, Y está más restringido que X. Todas las Y son X, pero no todas las X son Y.
En tu ejemplo, porque 'a' | 'b' podría ser 'a' o 'b' , ese tipo de unión no extiende el tipo 'a' , porque 'b' no sustituiría a 'a' . En cambio, 'a' extends ('a' | 'b') , porque todos los valores que coinciden con 'a' funcionarían donde 'a' | 'b' Se solicita 'a' | 'b' .
Como tal, A extends B si y sólo si A ⊆ B .
Una razón por la que esto es menos intuitivo en TypeScript es que su ejemplo trata de uniones de valores literales. Podría tener más sentido para nosotros pensar en esto en términos de objetos, donde {foo: number, bar: number} extends {foo: number} . El último, {foo: number} , podría tener una propiedad de bar de cualquier tipo o no tener ninguna bar . El anterior {foo: number, bar: number} es más específico y más restringido: no solo es foo un número, sino bar también es un número. Esto también coincide con el uso de extends en definiciones de clase o interfaz: la subclase o subinterfaz agrega propiedades y métodos, lo que restringe aún más las instancias en comparación con la superclase o la superinterfaz.
Esta es también la razón por la que never se puede asignar a todo : never es el tipo más restringido porque ningún valor real coincide con él, por lo que never se extiende a todo. El conjunto vacío es un subconjunto de todo conjunto ; el tipo vacío never es el subtipo de cada tipo y se puede asignar a cualquier otro tipo .
type Foo = { foo: number }; type Bar = never extends Foo ? true : false; // trueExtiende comprueba el tipo de instancia, no comprueba las posibles variables. Incluso si funciona, no tendría sentido para tipos complicados como clases. Si verifica eso con String, sería cierto.
let x: ('a' | 'b') extends String ? true : false;La salida sería;
truePuede consultar la documentación sobre tipos condicionales en https://www.typescriptlang.org/docs/handbook/2/conditional-types.html