# TypeScript Avanzado — Patrones y Ejemplos Completos

Referencia extendida de la skill `typescript-avanzado`. Contiene ejemplos completos
de conditional types, mapped types, template literal types, branded types,
extensión de módulo (module augmentation) y patrones compuestos.

---

## Conditional Types — Tipos que se Ramifican

```typescript
type EsString<T> = T extends string ? true : false;

type A = EsString<"hola">;   // true
type B = EsString<number>;   // false

// Distributive conditional types
type SinNull<T> = T extends null | undefined ? never : T;
type StringSinNull = SinNull<string | null | undefined>; // string

// Conditional types recursivos (TypeScript 4.1+)
type Aplanar<T> = T extends Array<infer Item> ? Aplanar<Item> : T;
type Anidado = Aplanar<number[][][]>;  // number

// Exclude e Extract implementados manualmente
type MiExclude<T, U> = T extends U ? never : T;
type MiExtract<T, U> = T extends U ? T : never;
type SoloStrings = MiExtract<string | number | boolean, string>; // string
```

---

## Mapped Types — Transformar la Forma de Tipos

```typescript
// Readonly personalizado
type InmutableProfundo<T> = {
  readonly [K in keyof T]: T[K] extends object ? InmutableProfundo<T[K]> : T[K];
};

// Hacer todos los campos opcionales excepto uno
type RequerirSolo<T, K extends keyof T> = Partial<T> & Required<Pick<T, K>>;

interface FormularioUsuario {
  nombre: string;
  email: string;
  telefono: string;
  direccion: string;
}

type FormularioConEmailRequerido = RequerirSolo<FormularioUsuario, "email">;

// Mapped type con remapping de claves (TypeScript 4.1+)
type Getters<T> = {
  [K in keyof T as `obtener${Capitalize<string & K>}`]: () => T[K];
};

interface Producto {
  nombre: string;
  precio: number;
}

type ProductoConGetters = Getters<Producto>;
// { obtenerNombre: () => string; obtenerPrecio: () => number }

// Filtrar propiedades por tipo
type SoloCadenas<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

interface Mixto { id: number; nombre: string; activo: boolean; correo: string }
type CamposString = SoloCadenas<Mixto>;
// { nombre: string; correo: string }
```

---

## Template Literal Types

```typescript
type Metodo = "GET" | "POST" | "PUT" | "DELETE";
type Version = "v1" | "v2";
type Recurso = "usuarios" | "productos" | "pedidos";

type RutaAPI = `/${Version}/${Recurso}`;

// Event names tipados
type EventosCRUD<T extends string> =
  | `${T}:creado`
  | `${T}:actualizado`
  | `${T}:eliminado`;

type EventosUsuario = EventosCRUD<"usuario">;

// CSS property validation
type CssPropColor = `color` | `background-color` | `border-color`;
type ValorColor = `#${string}` | `rgb(${number}, ${number}, ${number})`;

// Deep access con dot notation
type CaminoObjeto<T, K extends keyof T = keyof T> = K extends string
  ? T[K] extends Record<string, unknown>
    ? `${K}` | `${K}.${CaminoObjeto<T[K]>}`
    : `${K}`
  : never;

interface Config {
  base: { url: string; puerto: number };
  auth: { secreto: string };
}

type CaminosConfig = CaminoObjeto<Config>;
// "base" | "base.url" | "base.puerto" | "auth" | "auth.secreto"
```

---

## Branded Types — Primitivos con Semántica

```typescript
declare const marca: unique symbol;
type Branded<T, Marca> = T & { [marca]: Marca };

type IdUsuario   = Branded<string, "IdUsuario">;
type IdCuenta    = Branded<string, "IdCuenta">;
type Pesos       = Branded<number, "Pesos">;
type Dolares     = Branded<number, "Dolares">;

function crearIdUsuario(id: string): IdUsuario {
  return id as IdUsuario;
}

function transferir(
  origen: IdCuenta,
  destino: IdCuenta,
  monto: Pesos
): void {}

const idUsuario = crearIdUsuario("usr-123");
const idCuenta  = "cta-456" as IdCuenta;
const monto     = 100 as Pesos;

// transferir(idUsuario, idCuenta, monto); // Error de compilacion
transferir(idCuenta, idCuenta, monto);   // Correcto

// Branded types para validacion
type Email = Branded<string, "Email">;
type UrlSegura = Branded<string, "UrlSegura">;

function validarEmail(email: string): Email {
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
    throw new Error(`Email invalido: ${email}`);
  }
  return email as Email;
}
```

---

## Operador `satisfies` (TypeScript 4.9+)

```typescript
// Problema: as X elimina el narrowing de tipos literales
const colores = {
  rojo:  "#FF0000",
  verde: "#00FF00",
  azul:  "#0000FF",
} as Record<string, string>;
// colores.rojo es string — perdemos que es "#FF0000"

// BIEN — satisfies valida sin ampliar el tipo
const coloresValidados = {
  rojo:  "#FF0000",
  verde: "#00FF00",
  azul:  "#0000FF",
} satisfies Record<string, `#${string}`>;
// coloresValidados.rojo es "#FF0000" (literal)

// Caso real: configuracion de rutas
type ConfigRuta = {
  ruta: string;
  componente: string;
  guard?: string;
};

const rutas = [
  { ruta: "/inicio",    componente: "InicioComponent"   },
  { ruta: "/usuarios",  componente: "UsuariosComponent", guard: "AuthGuard" },
  { ruta: "/admin",     componente: "AdminComponent",    guard: "AdminGuard" },
] satisfies ConfigRuta[];
```

---

## Utility Types — La Caja de Herramientas

```typescript
interface Articulo {
  id: string;
  titulo: string;
  contenido: string;
  autor: string;
  publicado: boolean;
  fechaCreacion: Date;
}

type CrearArticulo = Omit<Articulo, "id" | "fechaCreacion">;
type ActualizarArticulo = Partial<Omit<Articulo, "id">> & Pick<Articulo, "id">;
type ArticuloPublico = Readonly<Omit<Articulo, "autor" | "publicado">>;

type ParamsCrear = Parameters<typeof crearArticulo>;
type RetornoCrear = ReturnType<typeof crearArticulo>;

type ValorAsync = Awaited<Promise<Promise<string>>>; // string
type CampoSeguro = NonNullable<string | null | undefined>; // string
```

---

## Extensión de Módulo (Module Augmentation)

```typescript
// Extender tipos de librerias de terceros
interface Array<T> {
  agruparPor<K extends PropertyKey>(
    selector: (elemento: T) => K
  ): Record<K, T[]>;
}

Array.prototype.agruparPor = function <T, K extends PropertyKey>(
  this: T[],
  selector: (elemento: T) => K
): Record<K, T[]> {
  return this.reduce((acumulador, elemento) => {
    const clave = selector(elemento);
    (acumulador[clave] ??= []).push(elemento);
    return acumulador;
  }, {} as Record<K, T[]>);
};

// Extender tipos de Express
declare global {
  namespace Express {
    interface Request {
      usuario?: { id: string; roles: string[] };
    }
  }
}

// Extender tipos de importaciones de modulos
declare module "*.svg" {
  const contenido: string;
  export default contenido;
}
```

---

## Patrones Compuestos — Builder tipado

```typescript
class ConstructorQuery<T extends object> {
  private filtros: Partial<T> = {};
  private camposOrden: (keyof T)[] = [];
  private limitePaginacion = 20;

  donde<K extends keyof T>(campo: K, valor: T[K]): this {
    this.filtros[campo] = valor;
    return this;
  }

  ordenarPor(...campos: (keyof T)[]): this {
    this.camposOrden = campos;
    return this;
  }

  limite(n: number): this {
    this.limitePaginacion = n;
    return this;
  }

  construir(): { filtros: Partial<T>; orden: (keyof T)[]; limite: number } {
    return {
      filtros: this.filtros,
      orden: this.camposOrden,
      limite: this.limitePaginacion,
    };
  }
}

interface Usuario { id: string; nombre: string; activo: boolean; edad: number }

const query = new ConstructorQuery<Usuario>()
  .donde("activo", true)
  .donde("edad", 30)
  .ordenarPor("nombre")
  .limite(10)
  .construir();
```
