A medida que avancemos en los tipos de TypeScript, necesitaremos herramientas que permitan combinar tipos existentes (para no crearlos desde 0). Esta es la 3ra parte de la Miniserie ¡Guía rápida de Melt: TypeScript! 🆃🆂 ✨.
ℹ️ Antes de seguir leyendo:
Recuerda que este post hace parte de una miniserie, y es probable que algunos temas ya hayan sido explicadas. Así que, te invitamos a revisar los posts anteriores:
TypeScript III
TALK DE LA SEMANA
Tipos de Intersección en TypeScript
En el post anterior vimos que las interfaces nos permiten construir nuevos tipos a partir de otros tipos al extenderlos, pero TypeScript nos proporciona una construcción diferente para combinar esos tipos existentes: intersecciones.
Intersección. Es la forma de componer tipos con otros tipos e inferir la herencia de estos.
| Un tipo de intersección se define con el operador: &.
Es decir, es la creación de un tipo que fusiona varios tipos para crear un solo tipo pero con la diferencia que este nuevo tipo infiere todas las propiedades de los otros tipos... 😪 me quedé sin aire. Mejor veamos cómo funciona:
Si existe algún error en el reproductor, puedes ver el video en el siguiente LINK.
Entre las dos formas de combinar tipos (interfaz e intersección), puede que nos parezca que son casi lo mismo, pero en realidad son sutilmente diferentes.
En cualquier caso, con las interfaces podríamos hacer extend desde otros tipos, y nombrar el resultado con un alias de tipo... Digamos que el resultado sería parecido a una intersección, no? Sin embargo, sólo veríamos los problemas una vez corramos el código, y es donde los conflictos marcarían las diferencias:
extends solo se puede usar con tipos con miembros estáticamente conocidos
type alphaType = {
a: string;
}
interface alphaInterface extends alphaType {} // ok
type ComplexType = { a: string } | { a: number };
// error: An interface can only extend an object type or intersection of object types...
interface ComplexInterface extends ComplexType {}
Cuando se extiende desde una interfaz principal, no puede crear campos con el mismo nombre que en la interfaz principal, pero con un tipo más amplio:
interface Parent {
a: string | number;
}
interface Child1 extends Parent {
a: string; // ok
}
interface Child2 extends Parent {
a: string | number | boolean; // error
}
Sin embargo, el tipo de intersección:
type IntersectedChild = Parent & {
a: string | boolean
};
// IntersectedChild will have property 'x' that is an intersection
// of 'string | number' and 'string | boolean', that is a 'string':
type IntersectedChildX = IntersectedChild['x']; // string
Entonces,
Cuando se usa extends se heredan todas las implementaciones del padre; sin embargo, la intersección de tipos solo funciona en el nivel de tipo.
La intersección de tipos es una operación de tipo, que se realiza con tipos cualquiera y dará un resultado; mientras que extends se limita a interfaces y tipos (y clases) similares a interfaces.
Y esa diferencia suele ser una de las razones principales por las que elegiría uno sobre el otro entre una interfaz y un alias de tipo de un tipo de intersección.
Tipos de unión en TypeScript
El sistema de tipos permite crear nuevos tipos a partir de tipos existentes con: combinaciones.
Unión. Es un tipo formado por dos o más, que representan valores que pueden ser any.
| Cuando uno quiere que una variable tenga múltiples tipos usa: Las uniones
Es fácil proporcionar un valor que coincida con un tipo de unión: simplemente proporcione un tipo que coincida con cualquiera de los miembros de la unión y si tiene un valor de un tipo de unión.
¿Cómo trabajar con los tipos de unión en TypeScript?
Si existe algún error en el reproductor, puedes ver el video en el siguiente LINK.
En el ejemplo, podemos conocer que padding tendría que ser de tipo Any para que acepte tanto string como number sin problema:
function padLeft(value: string, padding: any) {
if(typeof padding === 'number') {
return Array(padding + 1).join(' ') + value;
}
if(typeof padding === 'string'){
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
padLeft('Hello world', 4); // returns " Hello world"
Pero cuando reciba, por ejemplo, un booleano el código va fallar:
padLeft('Hello world', true); // throws at runtime
El tipo NO puede ser any, porque no recibe cualquiera sino numbers o strings
La idea es prevenir que de verdad la función pueda recibir cualquiera cuando en realidad sólo necesitamos dos tipos. La alternativa y solución para esta situación es usar una unión ( | ):
function typedPadLeft(value: string, padding: string | number): string {
if(typeof padding === 'number'){
return Array(padding + 1).join(' ') + value;
}
if(typeof padding === 'string') {
return padding + value;
}
throw new Error('Expected string or number, got '${padding}'.`);
}
typePadLeft('Hello world', 4); // returns " Hello world"
Y cuando reciba un booleano, el código va informar que: El dato tipo boolean no se le puede asignar a los tipos string o number:
typePadLeft('Hello world', true); //argument type 'bool' is not assignable
Recuerda que:
El Sistema de Tipos es responsable de verificar el tipo de datos de cualquier valor.
Y justamente por eso debemos programar usando en la menor cantidad posible el tipo: any. Acá te dejamos varios ejemplos:
Si existe algún error en el reproductor, puedes ver el video en el siguiente LINK.
En la siguiente parte de esta miniserie de TypeScript: The quick guide to the best TS tips: Classes, Generics & Utilities. Aprenderemos sobre las clases los genéricos y las utilidades de TS.
¡Gracias por leer!
📍 Conéctate con nosotros en instagram👇
Comments