Tipos Union y Guardas de Tipo
## Objetivos de Aprendizaje
- Crear y usar tipos union
- Trabajar con uniones discriminadas
- Dominar guardas de tipo
- Reducir tipos efectivamente
- Manejar null y undefined
## Tipos Union
### Sintaxis Basica
```typescript
let id: string | number;
id = "ABC123"; // OK
id = 456; // OK
// id = true; // Error
```
### En Funciones
```typescript
function printId(id: string | number): void {
console.log(`ID: ${id}`);
}
printId("ABC123"); // OK
printId(456); // OK
```
## Operaciones de Tipo Union
### Operaciones Limitadas
```typescript
function printId(id: string | number): void {
// id.toUpperCase(); // Error: number no tiene toUpperCase
if (typeof id === "string") {
console.log(id.toUpperCase()); // OK dentro del bloque
} else {
console.log(id.toFixed(2)); // OK dentro del bloque
}
}
```
## Uniones Discriminadas
### Propiedad Comun
```typescript
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
side: number;
}
type Shape = Circle | Square;
function area(shape: Shape): number {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2;
} else {
return shape.side ** 2;
}
}
```
### Ejemplo: Respuestas de API
```typescript
interface SuccessResponse {
status: "success";
data: User[];
}
interface ErrorResponse {
status: "error";
message: string;
}
type ApiResponse = SuccessResponse | ErrorResponse;
function handleResponse(response: ApiResponse): void {
if (response.status === "success") {
console.log(response.data);
} else {
console.error(response.message);
}
}
```
## Guardas de Tipo
### typeof
```typescript
function print(value: string | number): void {
if (typeof value === "string") {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
}
```
### instanceof
```typescript
class Dog {
bark(): void {
console.log("Guau!");
}
}
class Cat {
meow(): void {
console.log("Miau!");
}
}
function speak(animal: Dog | Cat): void {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
```
### Verificacion de Propiedad
```typescript
interface Book {
title: string;
author: string;
}
interface Magazine {
name: string;
issue: number;
}
function print(item: Book | Magazine): void {
if ("title" in item) {
console.log(item.title, item.author);
} else {
console.log(item.name, item.issue);
}
}
```
## Guardas de Tipo Personalizadas
### Funcion Retorna boolean
```typescript
interface Person {
name: string;
age: number;
}
interface Animal {
name: string;
species: string;
}
function isPerson(obj: any): obj is Person {
return "age" in obj && typeof obj.age === "number";
}
function check(obj: Person | Animal): void {
if (isPerson(obj)) {
console.log(obj.age);
} else {
console.log(obj.species);
}
}
```
### Predicado de Tipo
```typescript
function isString(value: unknown): value is string {
return typeof value === "string";
}
function process(value: unknown): void {
if (isString(value)) {
console.log(value.toUpperCase()); // TypeScript sabe que es string
}
}
```
## Null y Undefined
### Tipos Anulables
```typescript
let name: string | null = null;
name = "Alice";
// name = undefined; // Error si no esta en el tipo
```
### Encadenamiento Opcional
```typescript
interface User {
address?: {
city: string;
};
}
let user: User = {};
// Forma antigua
let city = user && user.address && user.address.city;
// Encadenamiento opcional
let city2 = user?.address?.city; // undefined si falta
```
### Fusion Nula
```typescript
let name: string | null = null;
// Valor por defecto cuando null/undefined
let displayName = name ?? "Anonimo";
// vs || (tambien captura cadena vacia)
let empty = "";
let result = empty ?? "por defecto"; // ""
let result2 = empty || "por defecto"; // "por defecto"
```
## Reduccion de Tipos
### En Bloque else
```typescript
function process(value: string | number | null): void {
if (value === null) {
console.log("valor null");
} else {
// TypeScript sabe: string | number
console.log(value);
}
}
```
### Despues de throw
```typescript
function process(value: string | null): void {
if (value === null) {
throw new Error("El valor es null");
}
// value es string aqui
console.log(value.toUpperCase());
}
```
### Tipo Never Despues de Agotamiento
```typescript
function process(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase();
}
// value es number aqui
return value.toFixed(2);
}
```
## Tipos Interseccion
### Sintaxis de Tipos Interseccion
```typescript
interface A {
a: string;
}
interface B {
b: number;
}
type AB = A & B;
let obj: AB = { a: "hola", b: 42 };
```
### Combinar Uniones
```typescript
type UnionA = "a" | "b";
type UnionB = 1 | 2;
type Combined = UnionA & UnionB;
// Combined = "a" & 1 | "a" & 2 | "b" & 1 | "b" & 2
// = never (en la mayoria de casos)
```
## typeof para Variables
```typescript
const config = {
host: "localhost",
port: 8080,
debug: true
};
type Config = typeof config;
// {
// host: string;
// port: number;
// debug: boolean;
// }
```
## Keyof
Obtener claves como tipo union:
```typescript
interface User {
id: number;
name: string;
email: string;
}
type UserKeys = keyof User;
// "id" | "name" | "email"
function getProperty(obj: T, key: K): T[K] {
return obj[key];
}
let user: User = { id: 1, name: "Alice", email: "alice@example.com" };
getProperty(user, "name"); // string
```
## Uniones Exhausivas
### Never en Default
```typescript
type Shape = Circle | Square | Triangle;
function area(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "square": return shape.side ** 2;
case "triangle": return 0.5 * shape.base * shape.height;
default: {
// Si anadimos una nueva forma, esto lo captura
const _exhaustive: never = shape;
return _exhaustive;
}
}
}
```
## Resumen
- Tipos union: `string | number` permite cualquiera
- Las uniones discriminadas usan una propiedad `kind` comun
- Las guardas de tipo reducen tipos: `typeof`, `instanceof`, verificaciones de propiedad
- Guardas personalizadas: retorno `obj is Type`
- Encadenamiento opcional: `obj?.prop?.nested`
- Fusion nula: `value ?? valorPorDefecto`
- Tipos interseccion: `A & B`
- Keyof obtiene union de claves de objeto
- Usar `never` para verificacion exhaustiva
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →