Saltar al contenido
webtype.orgwebtype.org

    ↑↓ mover · ⏎ abrir · esc cerrar

    Concepto 6 de 12

    Tipos condicionales

    Un tipo condicional es el if a nivel de tipos. La pregunta que plantea es siempre la misma: ¿es el lado izquierdo asignable al derecho? No «¿son iguales?», no «¿es una instancia de?» — asignable, y en un solo sentido.

    Sigue el tipoVerificado por el compilador
    ExpresiónIsText<"hello">
    Se resuelve atrue
    ExpresiónIsText<42>
    Se resuelve afalse

    extends comprueba la asignabilidad. Un literal de cadena toma la rama true; un número, la rama false.

    Definición del tipo
    type IsText<T> = T extends string ? true : false

    Asignabilidad, no igualdad

    type A = 'hello' extends string ? true : false   // true
    type B = string extends 'hello' ? true : false   // false
    type C = never extends string ? true : false     // true — never fits anywhere
    type D = { a: 1; b: 2 } extends { a: 1 } ? true : false  // true

    Un tipo más estrecho es asignable a uno más amplio, así que la prueba tiene dirección. Un objeto con propiedades de más es asignable a otro con menos, y por eso extends no sirve para comprobar que dos tipos objeto tengan la misma forma.

    Cuando de verdad necesitas «el mismo tipo», necesitas el helper Equal sobre el que se apoya cada comprobación de este sitio — dos condicionales diferidos comparados por identidad, más estricto que la asignabilidad en ambos sentidos.

    Las ramas se prueban en orden

    type Describe<T> = T extends (...args: never[]) => unknown
      ? 'function'
      : T extends unknown[]
        ? 'array'
        : T extends object
          ? 'object'
          : 'primitive'

    Los casos más estrechos deben ir primero. Funciones y arrays son ambos objetos, así que probar extends object pronto se los tragaría — la causa clásica de un DeepReadonly que destruye en silencio cada función que toca.

    El error más común

    // Intent: is T exactly string?
    type IsString<T> = T extends string ? true : false
    
    IsString<'hello'>  // true — but 'hello' is not string, it is narrower
    IsString<any>      // boolean — any matches both branches at once

    extends responde a «cabe en», así que todo literal de cadena pasa una prueba de string. Y any es asignable a todo, así que un condicional con any devuelve la unión de ambas ramas — un resultado que casi nunca es el que se pretendía.

    Ejercicio

    Responde true cuando T sea string, y false en caso contrario.

    Pruébalo

    2
    • IsString<'a'>—
      → true
    • IsString<number>—
      → false

    Para recordar

    Los condicionales preguntan por asignabilidad y prueban las ramas en orden. Lo estrecho primero, y recurre a Equal en cuanto necesites igualdad real.

    Se practica en