Lección 16 de 20 · Lectura y práctica
Quiz: Mejores Prácticas para Librerías
Tipo de lección: QuizConcepto claveLas mejores prácticas para librerías en TypeScript avanzado se centran en crear APIs tipadas de forma segura que sean flexibles para los usuarios pero restrictivas para prevenir errores. Esto.
Del concepto a la práctica
Taller de esta lección
Una buena API tipada limita usos ambiguos, infiere lo obvio y deja los errores cerca de la llamada. Este ejemplo compara una firma genérica con otra que pierde información.
Ejemplo resuelto
function first<T>(items: T[]): T | undefined { return items[0]; }
const title = first(["uno", "dos"]);
console.log(title?.toUpperCase()); // UNOLa inferencia obtiene string; no hace falta escribir first<string>.
Pruébalo
Llama
firstcon[10, 20].Comprueba que el resultado admite operaciones numéricas tras verificarlo.
Llama con un arreglo vacío.
Intenta declarar la función con
anyy compara la seguridad.
Comprueba tu resultado
El primer resultado debe ser 10 y el arreglo vacío debe producir undefined.
Error frecuente: devolver any, lo que permite llamar métodos inexistentes. Corrígelo con T | undefined, obligando al consumidor a considerar que no hay primer elemento y conservando el tipo concreto de la colección.
La función first no elimina undefined de los elementos: si T ya incluye undefined, ese valor puede estar presente incluso en una lista no vacía. Para10 utiliza result!==undefined antes de sumar1 y espera11. Las pruebas con errores intencionales deben usar un archivo aparte o @ts-expect-error para comprobar que existe un diagnóstico, evitando ocultarlo con any o una afirmación de tipo.

Mapa de prácticaUna condición decide qué camino ejecuta el programa. Un bucle repite una operación mientras queden elementos, y termina cuando la lista se agota. Separar ambas ideas ayuda a seguir el flujo y comprobar casos límite.
Ver la infografía en tamaño completo ↗Fuentes para profundizar
Tipo de lección: Quiz
Concepto clave
Las mejores prácticas para librerías en TypeScript avanzado se centran en crear APIs tipadas de forma segura que sean flexibles para los usuarios pero restrictivas para prevenir errores. Esto implica diseñar tipos que capturen las intenciones del desarrollador y guíen su uso correcto, similar a cómo un manual de instrucciones detallado evita malos usos de una herramienta compleja.
En arquitecturas complejas, una librería bien tipada actúa como un contrato de interfaz entre componentes, donde los tipos definen claramente qué datos se esperan y qué se devuelve. Esto reduce los bugs en tiempo de compilación y mejora la experiencia del desarrollador con autocompletado predictivo y validaciones automáticas.
Cómo funciona en la práctica
Imagina que estás construyendo una librería para manejar solicitudes HTTP con tipos genéricos. En lugar de usar any o tipos básicos, defines tipos parametrizados que se adaptan al contexto específico del usuario.
// Ejemplo: Función genérica para fetch tipado
type ApiResponse = {
data: T;
status: number;
};
async function fetchApi(url: string): Promise> {
const response = await fetch(url);
const data = await response.json();
return { data: data as T, status: response.status };
}
// Uso con tipo explícito
interface User {
id: number;
name: string;
}
const result = await fetchApi('/api/users/1');
// result.data es de tipo User, con autocompletado para .id y .name Este enfoque permite que la librería sea reutilizable en diferentes contextos mientras mantiene la seguridad de tipos, evitando errores comunes como acceder a propiedades inexistentes.
Caso de estudio
Considera una librería para validación de formularios donde los tipos reflejan las reglas de validación. En lugar de validar en tiempo de ejecución con comprobaciones manuales, los tipos garantizan que solo se pasen datos válidos.
// Definición de tipos para validación
type ValidationRule = (value: T) => boolean;
class Validator {
private rules: ValidationRule[] = [];
addRule(rule: ValidationRule): this {
this.rules.push(rule);
return this;
}
validate(value: T): boolean {
return this.rules.every(rule => rule(value));
}
}
// Uso con tipos específicos
const emailValidator = new Validator()
.addRule(value => value.includes('@'))
.addRule(value => value.length > 5);
// TypeScript infiere que emailValidator.validate() espera un string
const isValid = emailValidator.validate('test@example.com'); // true En este caso, los tipos previenen que se pasen números o objetos a la validación de emails, y el autocompletado ayuda a los usuarios a entender qué métodos están disponibles.
Errores comunes
- Sobrecarga de tipos genéricos: Definir demasiados parámetros genéricos que complican la API. Solución: Limitar a 2-3 genéricos por función y usar tipos condicionales para casos complejos.
- Tipos demasiado permisivos: Usar
anyounknownsin restricciones, lo que elimina los beneficios de TypeScript. Solución: Siempre preferir tipos específicos o genéricos acotados. - Documentación insuficiente de tipos: Asumir que los usuarios entenderán tipos complejos sin ejemplos. Solución: Incluir comentarios JSDoc y ejemplos de uso en la documentación.
- Ignorar la inferencia de tipos: Forzar a los usuarios a especificar tipos manualmente cuando TypeScript puede inferirlos. Solución: Diseñar APIs que aprovechen la inferencia automática.
- No probar tipos: Confiar solo en pruebas de ejecución sin validar los tipos. Solución: Usar herramientas como
tsdpara escribir pruebas de tipos.
Checklist de dominio
- ¿Tu librería usa tipos genéricos para hacer APIs flexibles pero seguras?
- ¿Los tipos previenen errores comunes como acceder a propiedades inexistentes?
- ¿La documentación incluye ejemplos de uso con tipos explícitos?
- ¿Has probado los tipos con herramientas como
tsdpara garantizar su corrección? - ¿Los usuarios pueden aprovechar la inferencia de tipos para reducir código boilerplate?
- ¿Los mensajes de error de TypeScript son claros y ayudan a depurar?
- ¿La librería sigue convenciones de nomenclatura consistentes para tipos (ej. sufijos como
Config,Options)?
Refactorizar una librería de utilidades con tipos seguros
En este ejercicio, refactorizarás una librería existente que maneja operaciones de array con tipos débiles para usar patrones genéricos y tipado profundo.
- Descarga o copia el siguiente código inicial que tiene problemas de tipado:
// Código inicial con tipos débiles
function filterArray(arr: any[], predicate: any): any[] {
return arr.filter(predicate);
}
function mapArray(arr: any[], mapper: any): any[] {
return arr.map(mapper);
}
// Uso problemático
const numbers = [1, 2, 3, 4];
const filtered = filterArray(numbers, (x) => x > 2); // Tipo: any[]
const mapped = mapArray(filtered, (x) => x.toString()); // Tipo: any[]- Refactoriza las funciones
filterArrayymapArraypara usar genéricos. Asegúrate de que: filterArrayacepte un array de tipoTy un predicado que devuelva un booleano paraT, devolviendo un array deT.mapArrayacepte un array de tipoTy un mapeador que transformeTaU, devolviendo un array deU.- Escribe ejemplos de uso que demuestren la seguridad de tipos, como operaciones encadenadas con autocompletado.
- Opcional: Añade tipos condicionales para manejar casos edge, como arrays vacíos o tipos nulos.
Pistas
- Usa
TyUcomo parámetros genéricos para representar los tipos de entrada y salida. - Considera usar
Array<T>como tipo de parámetro para mayor claridad. - Prueba tu refactorización con diferentes tipos de datos (ej. strings, objetos) para asegurar flexibilidad.
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.
Sigue con TypeScript Avanzado: Patrones Genéricos y Tipado Profundo para Arquitecturas Complejas
Esta lección forma parte de un curso de Cursalo. Revisa su programa y elige entre la compra individual o el acceso a toda la escuela.
Si ya tenías acceso a este curso, entra con tu cuenta: tus derechos y tu progreso se conservan. No necesitas comprarlo de nuevo.