TypeScript: Tipos Avanzados
Guia completa del sistema de tipos avanzados de TypeScript: genericos, tipos condicionales, tipos mapeados, literales de plantilla y tipos utilitarios. Ideal para construir aplicaciones con seguridad de tipos en tiempo de compilacion.
Descarga abierta · sin registro · para TypeScript, Node.js, Browser
/**
- CultivaSaaS — TypeScript Tipos Avanzados
- Referencia práctica de patrones type-safe para el dashboard de marketing con IA.
- Generado por CULTIVA IA · skill: typescript-tipos-avanzados */
// ============================================================ // 1. GENÉRICOS — API Client type-safe // ============================================================
/** Respuesta genérica del backend REST de CultivaSaaS */ interface ApiResponse { data: T; meta: { total: number; page: number; perPage: number; }; error?: string; }
/** Petición paginable genérica */ interface PaginatedRequest<TFilter = Record<string, unknown>> { page?: number; perPage?: number; filters?: TFilter; sortBy?: string; sortDir?: "asc" | "desc"; }
/** Cliente HTTP genérico type-safe */ async function apiGet( endpoint: string, params?: PaginatedRequest ): Promise<ApiResponse> { // Implementación real: fetch('/api/' + endpoint, { params }) return {} as ApiResponse; }
/** Extrae el tipo de datos de una ApiResponse */ type UnwrapApi = T extends ApiResponse ? D : never;
// Uso: // const res = await apiGet<Campaign[]>('campaigns', { page: 1, perPage: 20 }); // type CampaignData = UnwrapApi; // Campaign[]
// ============================================================ // 2. TIPOS CONDICIONALES — Validación de permisos por rol // ============================================================
type Role = "admin" | "manager" | "viewer";
/** Un admin puede hacer todo; manager, casi todo; viewer, solo leer */ type Permissions = R extends "admin" ? { read: true; write: true; delete: true; billing: true } : R extends "manager" ? { read: true; write: true; delete: false; billing: false } : { read: true; write: false; delete: false; billing: false };
type AdminPerms = Permissions<"admin">; // { read:true; write:true; delete:true; billing:true } type ViewerPerms = Permissions<"viewer">; // { read:true; write:false; delete:false; billing:false }
/** Extrae solo las keys donde el permiso es true */ type AllowedActions = { [K in keyof Permissions]: Permissions[K] extends true ? K : never; }[keyof Permissions];
type AdminActions = AllowedActions<"admin">; // "read" | "write" | "delete" | "billing" type ViewerActions = AllowedActions<"viewer">; // "read"
/** Inferencia del tipo de retorno de funciones de dominio */ type ReturnOf = T extends (...args: any[]) => infer R ? R : never;
function buildCampaignSummary(id: string) { return { id, status: "active" as const, clicks: 0, conversions: 0 }; }
type CampaignSummary = ReturnOf; // { id: string; status: "active"; clicks: number; conversions: number }
// ============================================================ // 3. TIPOS MAPEADOS — Entidades de CultivaSaaS // ============================================================
/** Modelo de campaña completo */ interface Campaign { id: string; name: string; status: "draft" | "active" | "paused" | "completed"; budget: number; startDate: Date; endDate: Date; channels: Channel[]; ownerId: string; createdAt: Date; updatedAt: Date; }
type Channel = "meta" | "google" | "tiktok" | "email" | "linkedin";
/** Formulario de creación: solo campos editables, todos opcionales */ type CreateCampaignForm = Partial< Omit<Campaign, "id" | "createdAt" | "updatedAt">
;
/** Vista pública de campaña: sin datos sensibles de owner */ type CampaignPublicView = Omit<Campaign, "ownerId" | "budget">;
/** Versión readonly para componentes de solo lectura */ type CampaignReadonly = Readonly;
/** Genera getters tipados para cualquier entidad */
type Getters = {
[K in keyof T as get${Capitalize<string & K>}]: () => T[K];
};
type CampaignGetters = Getters<Pick<Campaign, "id" | "name" | "status">>; // { getId: () => string; getName: () => string; getStatus: () => "draft"|"active"|"paused"|"completed" }
/** Filtra solo las propiedades de tipo string de un objeto */ type StringProps = { [K in keyof T as T[K] extends string ? K : never]: T[K]; };
type CampaignStringFields = StringProps; // { id: string; name: string; status: ...; ownerId: string }
/** Marca campos como "sucios" para el formulario */ type DirtyFields = { [K in keyof T]?: boolean; };
type CampaignDirty = DirtyFields;
// ============================================================ // 4. TEMPLATE LITERAL TYPES — Eventos y rutas type-safe // ============================================================
/** Eventos de dominio generados automáticamente */ type CampaignEvent = | "click" | "impression" | "conversion" | "lead" | "purchase";
type CampaignEventHandler = on${Capitalize<CampaignEvent>};
// "onClick" | "onImpression" | "onConversion" | "onLead" | "onPurchase"
/** Rutas de API construidas desde los recursos */ type Resource = "campaign" | "user" | "metric" | "report"; type HttpMethod = "get" | "post" | "put" | "delete";
type ApiRoute = /${Resource}s | /${Resource}s/:id;
// "/campaigns" | "/campaigns/:id" | "/users" | "/users/:id" | ...
type ApiEndpoint = ${Uppercase<HttpMethod>} ${ApiRoute};
// "GET /campaigns" | "POST /campaigns" | "GET /campaigns/:id" | ...
/** Paths de configuración anidada type-safe */ interface AppConfig { api: { baseUrl: string; timeout: number; retries: number; }; feature: { aiOptimization: boolean; multiChannel: boolean; }; theme: { primaryColor: string; darkMode: boolean; }; }
type NestedPath<T, Prefix extends string = ""> = {
[K in keyof T & string]: T[K] extends object
? NestedPath<T[K], ${Prefix}${K}.>
: ${Prefix}${K};
}[keyof T & string];
type ConfigPath = NestedPath; // "api.baseUrl" | "api.timeout" | "api.retries" | // "feature.aiOptimization" | "feature.multiChannel" | // "theme.primaryColor" | "theme.darkMode"
/** Acceso type-safe a la config por path */
type GetByPath<T, P extends string> =
P extends ${infer K}.${infer Rest}
? K extends keyof T
? GetByPath<T[K], Rest>
: never
: P extends keyof T
? T[P]
: never;
type TimeoutType = GetByPath<AppConfig, "api.timeout">; // number type DarkModeType = GetByPath<AppConfig, "theme.darkMode">; // boolean
// ============================================================ // 5. UTILITY TYPES — Sistema de métricas tipado // ============================================================
/** Métrica de rendimiento de campaña */ interface CampaignMetric { campaignId: string; channel: Channel; date: Date; impressions: number; clicks: number; conversions: number; spend: number; revenue: number; ctr: number; roas: number; }
/** Solo las métricas numéricas (para gráficas) */ type NumericMetrics = { [K in keyof CampaignMetric as CampaignMetric[K] extends number ? K : never]: number; }; // { impressions, clicks, conversions, spend, revenue, ctr, roas }
/** Resumen de métricas: campos requeridos mínimos */ type MetricSummary = Required<Pick<CampaignMetric, "impressions" | "clicks" | "conversions" | "roas">>;
/** Mapa de métricas por canal */ type MetricsByChannel = Record<Channel, NumericMetrics>;
/** Estado del store de métricas */ type MetricStore = { byId: Record<string, CampaignMetric>; byCampaign: Record<string, CampaignMetric[]>; loading: boolean; error: string | null; selectedChannels: Channel[]; };
// ============================================================ // BONUS: Type Guards y Discriminated Unions // ============================================================
/** Estados de una campaña con datos adicionales por estado */ type CampaignState = | { status: "draft"; draftData: { savedAt: Date } } | { status: "active"; activeData: { launchedAt: Date; dailyBudget: number } } | { status: "paused"; pausedData: { reason: string; pausedAt: Date } } | { status: "completed"; completedData: { finalReport: string } };
/** Type guard para campaña activa */ function isActiveCampaign( state: CampaignState ): state is Extract<CampaignState, { status: "active" }> { return state.status === "active"; }
/** Type guard genérico con key check */ function hasProperty<T extends object, K extends PropertyKey>( obj: T, key: K ): obj is T & Record<K, unknown> { return key in obj; }
// Uso en componente React (sin JSX para claridad):
function processCampaignState(state: CampaignState) {
if (isActiveCampaign(state)) {
// TypeScript sabe que state.activeData existe
const { dailyBudget } = state.activeData;
return Activa — presupuesto diario: ${dailyBudget}€;
}
if (state.status === "completed") {
return Completada — reporte: ${state.completedData.finalReport};
}
return Estado: ${state.status};
}
// ============================================================ // BONUS 2: Tipos de Testing // ============================================================
/** Utilidad para verificar igualdad de tipos en tiempo de compilación */ type AssertEqual<T, U> = [T] extends [U] ? [U] extends [T] ? true : false : false;
// Tests de tipos (fallarían en compilación si algo está mal): type _test1 = AssertEqual<MetricSummary["impressions"], number>; // true type _test2 = AssertEqual<AllowedActions<"viewer">, "read">; // true type _test3 = AssertEqual<GetByPath<AppConfig, "api.timeout">, number>; // true type _test4 = AssertEqual<CampaignEventHandler, "onClick" | "onImpression" | "onConversion" | "onLead" | "onPurchase">; // true
export type { ApiResponse, Campaign, CampaignMetric, CampaignState, CampaignEvent, CampaignEventHandler, AppConfig, ConfigPath, Role, Permissions, MetricsByChannel, MetricStore, };
// qué_hace
Enseña a dominar el sistema de tipos avanzados de TypeScript para construir aplicaciones robustas y type-safe.
// cómo_lo_hace
Proporciona ejemplos practicos y patrones reutilizables de genericos, tipos condicionales, mapeados, literales de plantilla y utility types con buenas practicas y errores comunes.
// ejemplo_de_uso
Para desarrolladores TypeScript que necesitan modelar estructuras de datos complejas con garantías de tipo en tiempo de compilación. Ej.: usar tipos mapeados y condicionales para construir un validador genérico que infiera el tipo de respuesta de cada endpoint de tu API.
// plataformas
// opiniones_de_la_comunidad
Opiniones
Cargando opiniones…
// pase_cultiva_ia
Llévate todo el arsenal con el Pase
Todas las skills, prompts y automatizaciones del catálogo en un único archivo, listas para usar: un pago, acceso de por vida y las novedades que añadamos. Sin suscripción.
Pago único · IVA incluido · pago seguro con Stripe.
Acceso inmediato · si no es lo que esperabas, te devolvemos los 10 €.