Búsqueda de sitios web

¿Por qué TypeScript muestra un error en este escenario con un tipo unión?


En este escenario:

type Type = {
    a: string;
    b: string;
} | {
    a: number;
    b: number;
};

const func = ({a, b}: Type): string => {
    if (typeof a === 'string') {
        return b;
    }

    return 'w/e';
}

func({a: 1, b: 'w/e'});

Obtengo un error en return b; que indica

El tipo 'string | number' no se puede asignar al tipo 'string'.

El tipo 'number' no se puede asignar al tipo 'string'.

Por lo que entiendo por el error, Typescript interpreta el tipo Type como

type Type = {
    a: string | number;
    b: string | number;
};

, pero incluso si ese fuera el caso, ¿por qué no puedo llamar a la función func con una cadena y un número?

El error en la llamada indica

El argumento de tipo '{ a: number; b: string; }' no se puede asignar al parámetro de tipo 'Type'.

El tipo ' { a: number; b: string; } ' no se puede asignar al tipo ' { a: number; b: number; } '.

Tipos de propiedad 'b' son incompatibles.

El tipo 'string' no se puede asignar al tipo 'number'.

(versión de ts: 4.7.2)

Enlace del parque

  • Editar después del comentario de UnholySheep:

La llamada a la función con los tipos incorrectos fue solo para probar mi hipótesis sobre por qué obtengo el error anterior. Lo que no entiendo es por qué obtengo un error en return b;.

Respuesta #1

Ok, la respuesta es Discriminated Unions. Tengo que usar un discriminante, una propiedad común en cada elemento de la unión. Esa propiedad discriminante:

  1. Tiene que ser un tipo primitivo, y
  2. Debe tener un valor específico y no el tipo de ese valor. Por ejemplo, isPrimary: true, no isPrimary: boolean.

Entonces puedo verificar el valor de esa propiedad para asumir los tipos de las otras propiedades.

Además, en las versiones de Typescript < 4.6, si tengo un objeto con un tipo especificado como el anterior, si lo desestructuro, entonces las nuevas variables se consideran independientes y seguiré obteniendo un error similar al ejemplo inicial. La desestructuración funciona en la versión 4.6 o superior.

  • Ejemplo de parque infantil (versión 4.6)

  • Ejemplo de parque infantil (versión 4.5)

Enlaces útiles:

  • El enlace de @Austaras al artículo de Microsoft anunciando el soporte para desestructuración
  • Comprendiendo las Uniones Discriminadas
  • Tipos Singleton de Typescript

Gracias @Austaras y @coglialoro

Respuesta #2

TS no puede rastrear la relación de a y b después de que los desestructures, por lo que ambos se inferirán como string | number. TS 4.6 introdujo un análisis de flujo de control limitado para la desestructuración, que solo se aplica a uniones discriminadas.

Artículos relacionados