¿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:
- Tiene que ser un tipo primitivo, y
- Debe tener un valor específico y no el tipo de ese valor. Por ejemplo,
isPrimary: true, noisPrimary: 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.