TypeScript
Tutorial TypeScript: Tipos genéricos básicos
Aprende tipos genéricos en TypeScript para crear funciones flexibles, aplicar restricciones y manejar múltiples parámetros con seguridad de tipos.
Aprende TypeScript y certifícateSintaxis genérica <T>
Los tipos genéricos son una de las características más potentes de TypeScript, permitiendo crear componentes reutilizables que pueden trabajar con una variedad de tipos en lugar de un único tipo específico. La sintaxis genérica se identifica mediante el uso de los corchetes angulares <>
y un parámetro de tipo que convencionalmente se representa con la letra T
(aunque puede utilizarse cualquier identificador válido).
La idea fundamental detrás de los genéricos es permitir que especifiques un contrato entre el tipo de datos que proporcionas y el que esperas recibir, manteniendo la seguridad de tipos durante todo el proceso.
Declaración básica de genéricos
La sintaxis básica para declarar un tipo genérico utiliza los corchetes angulares <T>
donde T
actúa como un parámetro de tipo:
function identity<T>(arg: T): T {
return arg;
}
En este ejemplo, la función identity
acepta un argumento de cualquier tipo y devuelve un valor del mismo tipo. El parámetro T
captura el tipo proporcionado, permitiendo usarlo en múltiples lugares dentro de la función.
Uso de tipos genéricos
Existen dos formas principales de invocar una función genérica:
// 1. Especificando explícitamente el tipo
let output1 = identity<string>("Hello, TypeScript");
// 2. Usando inferencia de tipos (más común)
let output2 = identity("Hello, TypeScript"); // TypeScript infiere que T es string
La segunda forma es más concisa y generalmente preferida, ya que TypeScript puede inferir el tipo automáticamente basándose en el argumento proporcionado.
Genéricos con interfaces
Los genéricos también pueden aplicarse a interfaces para crear estructuras de datos flexibles:
interface Box<T> {
value: T;
}
// Uso con un tipo específico
let stringBox: Box<string> = { value: "TypeScript" };
let numberBox: Box<number> = { value: 42 };
Esta interfaz Box
puede contener cualquier tipo de valor, manteniendo la información de tipo durante todo el código.
Genéricos con clases
De manera similar, podemos crear clases genéricas:
class Container<T> {
private item: T;
constructor(item: T) {
this.item = item;
}
getItem(): T {
return this.item;
}
}
// Instanciación con diferentes tipos
const numberContainer = new Container<number>(123);
const stringContainer = new Container("Hello"); // Inferencia de tipos
Genéricos con tipos y alias
Los genéricos también funcionan con alias de tipos:
type Pair<T, U> = {
first: T;
second: U;
};
// Uso del alias genérico
const coordinates: Pair<number, number> = { first: 10, second: 20 };
const entry: Pair<string, boolean> = { first: "isActive", second: true };
Parámetros de tipo por defecto
TypeScript permite especificar valores por defecto para los parámetros de tipo:
interface ApiResponse<T = any> {
data: T;
status: number;
message: string;
}
// Sin especificar el tipo, usa el valor por defecto (any)
const genericResponse: ApiResponse = {
data: { whatever: "anything" },
status: 200,
message: "OK"
};
// Especificando un tipo concreto
interface User {
id: number;
name: string;
}
const userResponse: ApiResponse<User> = {
data: { id: 1, name: "John" },
status: 200,
message: "User found"
};
Convenciones de nomenclatura
Aunque T
es la convención más común para un parámetro de tipo genérico, existen otras convenciones útiles:
// T para Tipo general
function process<T>(x: T): T { return x; }
// K para Key (Clave), V para Value (Valor)
function getProperty<K extends keyof T, T>(obj: T, key: K): T[K] {
return obj[key];
}
// E para Element (Elemento en colecciones)
function firstElement<E>(arr: E[]): E | undefined {
return arr[0];
}
Estas convenciones ayudan a que el código sea más legible y expresivo, especialmente cuando se utilizan múltiples parámetros de tipo.
Genéricos con arreglos y colecciones
Los genéricos son particularmente útiles para trabajar con colecciones:
function getFirstElement<T>(array: T[]): T | undefined {
return array.length > 0 ? array[0] : undefined;
}
const numbers = [1, 2, 3, 4, 5];
const firstNumber = getFirstElement(numbers); // Tipo: number | undefined
const names = ["Alice", "Bob", "Charlie"];
const firstName = getFirstElement(names); // Tipo: string | undefined
Ventajas de usar genéricos
Los genéricos ofrecen varias ventajas importantes:
- Reutilización de código: Escribe una función o clase una vez y úsala con diferentes tipos.
- Seguridad de tipos: Mantiene la información de tipo en toda la aplicación.
- Flexibilidad: Permite crear componentes que se adaptan a diferentes contextos.
- Autocompletado: El editor puede sugerir propiedades y métodos específicos del tipo.
La sintaxis genérica <T>
es el fundamento para crear código TypeScript flexible y reutilizable, permitiendo escribir componentes que funcionan con una variedad de tipos mientras se mantiene la seguridad de tipos estáticos.
Funciones genéricas
Las funciones genéricas representan una de las aplicaciones más prácticas y comunes de los genéricos en TypeScript. Estas funciones nos permiten escribir código reutilizable que puede operar con diferentes tipos de datos mientras mantiene la seguridad de tipos en tiempo de compilación.
Estructura básica de una función genérica
Una función genérica se define añadiendo un parámetro de tipo entre corchetes angulares antes de la lista de parámetros de la función:
function nombreFuncion<T>(parametro: T): T {
// Implementación
return parametro;
}
El parámetro de tipo T
actúa como un marcador de posición que será reemplazado por un tipo real cuando se invoque la función.
Casos de uso prácticos
Las funciones genéricas son especialmente útiles cuando necesitamos mantener la información de tipo a través de operaciones:
function reverseArray<T>(items: T[]): T[] {
return [...items].reverse();
}
// Uso con diferentes tipos
const numbers = reverseArray([1, 2, 3, 4, 5]); // tipo: number[]
const names = reverseArray(["Ana", "Carlos", "Berta"]); // tipo: string[]
const mixed = reverseArray([true, 42, "hola"]); // tipo: (string | number | boolean)[]
En este ejemplo, la función reverseArray
preserva el tipo de los elementos del array, independientemente de qué tipo sea.
Múltiples parámetros de tipo
Podemos definir funciones genéricas con varios parámetros de tipo cuando necesitamos trabajar con diferentes tipos en la misma función:
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const result = merge(
{ name: "Elena" },
{ age: 28 }
); // tipo: { name: string; age: number; }
console.log(result.name); // "Elena"
console.log(result.age); // 28
Esta función combina dos objetos y TypeScript infiere correctamente que el resultado tendrá las propiedades de ambos objetos.
Inferencia de tipos en funciones genéricas
TypeScript puede inferir automáticamente los tipos genéricos basándose en los argumentos proporcionados:
function wrapInArray<T>(value: T): T[] {
return [value];
}
// Inferencia automática
const stringArray = wrapInArray("TypeScript"); // tipo: string[]
const numberArray = wrapInArray(42); // tipo: number[]
Aunque también podemos especificar explícitamente el tipo:
const explicitArray = wrapInArray<boolean>(true); // tipo: boolean[]
Funciones genéricas con tipos de retorno específicos
Podemos definir funciones genéricas donde el tipo de retorno sea diferente del tipo de entrada:
function convertToString<T>(value: T): string {
return String(value);
}
const numAsString = convertToString(123); // tipo: string
Funciones genéricas con arrow functions
Las funciones flecha también pueden ser genéricas:
const getFirstItem = <T>(array: T[]): T | undefined => {
return array.length > 0 ? array[0] : undefined;
};
const first = getFirstItem([10, 20, 30]); // tipo: number | undefined
Nota: En JSX, puede ser necesario añadir una coma después del parámetro de tipo para evitar ambigüedades con las etiquetas:
const getFirstItem = <T,>(array: T[]): T | undefined => {
return array.length > 0 ? array[0] : undefined;
};
Funciones genéricas como parámetros
Podemos pasar funciones genéricas como parámetros a otras funciones:
function executeOperation<T, U>(
data: T,
operation: (x: T) => U
): U {
return operation(data);
}
const lengthResult = executeOperation("hello", (x) => x.length); // tipo: number
const doubleResult = executeOperation(5, (x) => x * 2); // tipo: number
Funciones genéricas con tipos condicionales
Podemos combinar funciones genéricas con tipos condicionales para crear comportamientos más sofisticados:
function process<T>(value: T): T extends string ? string : number {
if (typeof value === "string") {
return value.toUpperCase() as any;
} else {
return (typeof value === "number" ? value * 2 : 0) as any;
}
}
const stringResult = process("hello"); // tipo: string
const numberResult = process(10); // tipo: number
Funciones genéricas con promesas
Las funciones genéricas son especialmente útiles cuando trabajamos con promesas:
async function fetchData<T>(url: string): Promise<T> {
const response = await fetch(url);
return response.json();
}
interface User {
id: number;
name: string;
}
// El tipo de retorno será Promise<User[]>
const users = await fetchData<User[]>('/api/users');
// TypeScript sabe que users es de tipo User[]
users.forEach(user => console.log(user.name));
Funciones genéricas con restricciones
Aunque las restricciones se verán con más detalle en otra sección, es importante mencionar que podemos limitar los tipos que pueden usarse con una función genérica:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { id: 1, name: "Sara", role: "Admin" };
const userName = getProperty(user, "name"); // tipo: string
// getProperty(user, "age"); // Error: 'age' no existe en el objeto
Buenas prácticas para funciones genéricas
- Nombres descriptivos: Usa nombres de parámetros de tipo que reflejen su propósito (
TInput
,TOutput
, etc.) cuandoT
no sea suficientemente descriptivo. - Inferencia cuando sea posible: Permite que TypeScript infiera los tipos automáticamente para hacer el código más limpio.
- Restricciones apropiadas: Usa restricciones para evitar errores en tiempo de ejecución.
- Documentación: Documenta el propósito y los requisitos de tus funciones genéricas, especialmente si tienen comportamientos complejos.
Las funciones genéricas son una herramienta fundamental en TypeScript que nos permite escribir código más flexible y reutilizable sin sacrificar la seguridad de tipos, lo que resulta en aplicaciones más robustas y mantenibles.
Restricciones con extends
Cuando trabajamos con tipos genéricos en TypeScript, a veces necesitamos limitar los tipos que pueden utilizarse como argumentos genéricos. Las restricciones nos permiten especificar que un tipo debe cumplir ciertos requisitos, lo que nos ayuda a crear código más seguro y predecible.
La palabra clave extends
es la que nos permite definir estas restricciones, estableciendo una relación de "es un subtipo de" o "cumple con la interfaz de".
Sintaxis básica de restricciones
La sintaxis para aplicar restricciones utiliza la palabra clave extends
después del parámetro de tipo:
function ejemplo<T extends TipoBase>(arg: T): T {
// Implementación
return arg;
}
Donde TipoBase
representa el tipo mínimo que debe cumplir T
.
Restricciones con interfaces
Una de las aplicaciones más comunes es restringir un tipo genérico para que cumpla con una interfaz específica:
interface ConNombre {
nombre: string;
}
function imprimirNombre<T extends ConNombre>(objeto: T): void {
console.log(objeto.nombre);
}
// Funciona porque el objeto tiene la propiedad 'nombre'
imprimirNombre({ nombre: "Ana", edad: 30 });
// Error: el argumento no tiene la propiedad 'nombre'
// imprimirNombre({ edad: 25 });
En este ejemplo, la función imprimirNombre
solo acepta tipos que tengan al menos una propiedad nombre
de tipo string
.
Restricciones con tipos primitivos
También podemos restringir los tipos genéricos a ciertos tipos primitivos:
function convertirAString<T extends string | number | boolean>(valor: T): string {
return String(valor);
}
const resultado1 = convertirAString("hola"); // Funciona
const resultado2 = convertirAString(42); // Funciona
const resultado3 = convertirAString(true); // Funciona
// Error: el tipo 'object' no satisface la restricción
// const resultado4 = convertirAString({});
Restricciones con uniones de tipos
Las restricciones pueden utilizar uniones de tipos para permitir múltiples tipos base:
type Numerico = number | bigint;
function duplicar<T extends Numerico>(valor: T): T {
// Usamos 'as T' porque TypeScript no puede inferir que el resultado
// sigue siendo del mismo tipo específico
return (valor + valor) as T;
}
const num = duplicar(5); // tipo: number
const bigNum = duplicar(10n); // tipo: bigint
Restricciones con tipos genéricos anidados
Podemos aplicar restricciones a tipos genéricos que a su vez contienen otros genéricos:
function procesarArray<T extends Array<string | number>>(items: T): T {
console.log(`Procesando ${items.length} elementos`);
return items;
}
const strArray = procesarArray(["a", "b", "c"]); // Funciona
const numArray = procesarArray([1, 2, 3]); // Funciona
const mixedArray = procesarArray(["a", 1, "b", 2]); // Funciona
// Error: el tipo 'boolean[]' no satisface la restricción
// const boolArray = procesarArray([true, false]);
Restricciones con keyof
El operador keyof
combinado con restricciones genéricas nos permite crear funciones que operan de forma segura con las propiedades de un objeto:
function obtenerPropiedad<T, K extends keyof T>(obj: T, clave: K): T[K] {
return obj[clave];
}
const usuario = {
id: 123,
nombre: "Carlos",
email: "carlos@ejemplo.com"
};
const id = obtenerPropiedad(usuario, "id"); // tipo: number
const nombre = obtenerPropiedad(usuario, "nombre"); // tipo: string
// Error: el argumento de tipo '"edad"' no es asignable al parámetro de tipo 'keyof...'
// const edad = obtenerPropiedad(usuario, "edad");
Esta técnica es extremadamente útil para crear funciones que acceden a propiedades de objetos de forma segura.
Restricciones con clases
Podemos restringir tipos genéricos para que sean instancias de una clase específica:
class Animal {
nombre: string;
constructor(nombre: string) {
this.nombre = nombre;
}
hacerSonido(): void {
console.log("Algún sonido");
}
}
class Perro extends Animal {
raza: string;
constructor(nombre: string, raza: string) {
super(nombre);
this.raza = raza;
}
hacerSonido(): void {
console.log("Guau guau");
}
}
function crearAnimal<T extends Animal>(Constructor: new (...args: any[]) => T, nombre: string): T {
return new Constructor(nombre);
}
const miPerro = crearAnimal(Perro, "Rex"); // tipo: Perro
miPerro.hacerSonido(); // "Guau guau"
Restricciones con tipos condicionales
Las restricciones pueden combinarse con tipos condicionales para crear comportamientos más sofisticados:
type EsArray<T> = T extends any[] ? true : false;
function procesarEntrada<T, R extends EsArray<T> extends true ? T : T[]>(
entrada: T,
comoArray: R
): void {
const array = comoArray ? (entrada as any) : [entrada];
console.log(`Procesando ${array.length} elementos`);
}
Restricciones con genéricos por defecto
Podemos combinar restricciones con valores por defecto para los parámetros de tipo:
interface OpcionesBase {
timeout?: number;
cache?: boolean;
}
function configurar<T extends OpcionesBase = OpcionesBase>(opciones: T): void {
console.log(`Timeout: ${opciones.timeout ?? 1000}ms`);
console.log(`Cache: ${opciones.cache ?? true}`);
}
// Funciona con las opciones mínimas
configurar({});
// Funciona con opciones adicionales
configurar({ timeout: 2000, cache: false, debug: true });
Restricciones con tipos recursivos
Las restricciones también pueden aplicarse a tipos recursivos:
type ElementoAnidado<T> = T | Array<ElementoAnidado<T>>;
function aplanar<T, U extends ElementoAnidado<T>>(entrada: U): T[] {
if (Array.isArray(entrada)) {
return entrada.flatMap(item => aplanar(item));
} else {
return [entrada as T];
}
}
const resultado = aplanar([1, [2, [3, 4]], 5]); // [1, 2, 3, 4, 5]
Beneficios de usar restricciones
Las restricciones con extends
ofrecen varios beneficios importantes:
- Seguridad de tipos: Evitan errores en tiempo de compilación al garantizar que los tipos cumplen con ciertos requisitos.
- Mejor autocompletado: El editor puede sugerir propiedades y métodos disponibles basados en las restricciones.
- Código más expresivo: Las restricciones documentan implícitamente los requisitos de tus funciones y tipos.
- Detección temprana de errores: Los problemas se detectan durante la compilación en lugar de en tiempo de ejecución.
Consideraciones al usar restricciones
- Balance entre flexibilidad y seguridad: Restricciones demasiado estrictas pueden limitar la reutilización, mientras que restricciones demasiado laxas pueden reducir la seguridad.
- Legibilidad: Las restricciones complejas pueden hacer que el código sea más difícil de entender.
- Rendimiento de compilación: Las restricciones muy complejas pueden aumentar el tiempo de compilación.
Las restricciones con extends
son una herramienta fundamental para crear APIs genéricas robustas en TypeScript, permitiéndonos encontrar un equilibrio entre flexibilidad y seguridad de tipos.
Múltiples genéricos
Los genéricos en TypeScript se vuelven aún más potentes cuando utilizamos múltiples parámetros de tipo en una misma estructura. Esta técnica nos permite crear componentes altamente flexibles que pueden manejar diferentes tipos de datos simultáneamente, manteniendo la seguridad de tipos en todo momento.
Sintaxis básica con múltiples parámetros
La sintaxis para declarar múltiples parámetros de tipo es sencilla: simplemente separamos cada parámetro con una coma dentro de los corchetes angulares:
function ejemplo<T, U>(primerArg: T, segundoArg: U): [T, U] {
return [primerArg, segundoArg];
}
En este ejemplo, T
y U
son parámetros de tipo independientes que pueden representar cualquier tipo.
Casos de uso prácticos
Funciones con múltiples tipos
Una aplicación común es crear funciones que procesen diferentes tipos de entrada y produzcan resultados tipados correctamente:
function combinar<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const persona = { nombre: "Laura" };
const datos = { edad: 29, ciudad: "Barcelona" };
const resultado = combinar(persona, datos);
// resultado tiene tipo: { nombre: string; edad: number; ciudad: string; }
console.log(resultado.nombre); // "Laura"
console.log(resultado.edad); // 29
Mapeo entre tipos diferentes
Los múltiples genéricos son ideales para transformar datos de un tipo a otro:
function transformar<T, U>(
items: T[],
transformador: (item: T) => U
): U[] {
return items.map(transformador);
}
const numeros = [1, 2, 3, 4, 5];
const numerosDuplicados = transformar(numeros, n => n * 2);
// numerosDuplicados: number[]
const numerosComoTexto = transformar(numeros, n => n.toString());
// numerosComoTexto: string[]
Interfaces con múltiples genéricos
Las interfaces también pueden definirse con varios parámetros de tipo:
interface Par<K, V> {
clave: K;
valor: V;
}
const coordenada: Par<string, number> = { clave: "latitud", valor: 41.3851 };
const configuracion: Par<string, boolean> = { clave: "habilitado", valor: true };
Esta técnica es especialmente útil para crear estructuras de datos como diccionarios o mapas:
interface Diccionario<K extends string | number | symbol, V> {
[clave: K]: V;
obtener(clave: K): V | undefined;
establecer(clave: K, valor: V): void;
}
Clases con múltiples genéricos
Las clases pueden aprovechar múltiples genéricos para crear estructuras de datos versátiles:
class Coleccion<T, U> {
private items: Array<[T, U]> = [];
agregar(clave: T, valor: U): void {
this.items.push([clave, valor]);
}
obtenerPorClave(clave: T): U | undefined {
const par = this.items.find(item => item[0] === clave);
return par ? par[1] : undefined;
}
}
// Uso con diferentes combinaciones de tipos
const usuarios = new Coleccion<number, string>();
usuarios.agregar(1, "Carlos");
usuarios.agregar(2, "Ana");
const nombreUsuario = usuarios.obtenerPorClave(1); // tipo: string | undefined
Tipos alias con múltiples genéricos
Los alias de tipo también pueden utilizar múltiples parámetros:
type Resultado<T, E> = {
exito: true;
valor: T;
} | {
exito: false;
error: E;
};
function dividir(a: number, b: number): Resultado<number, string> {
if (b === 0) {
return { exito: false, error: "No se puede dividir por cero" };
}
return { exito: true, valor: a / b };
}
const resultado = dividir(10, 2);
if (resultado.exito) {
console.log(`El resultado es: ${resultado.valor}`);
} else {
console.log(`Error: ${resultado.error}`);
}
Este patrón es muy útil para manejar operaciones que pueden fallar, similar al tipo Result
en lenguajes como Rust.
Genéricos anidados
Podemos combinar múltiples genéricos con estructuras anidadas para crear tipos más complejos:
interface Respuesta<T, M extends { [key: string]: any }> {
datos: T;
metadata: M;
timestamp: number;
}
type PaginacionMetadata = {
pagina: number;
total: number;
porPagina: number;
};
// Uso con tipos específicos
const respuestaPaginada: Respuesta<string[], PaginacionMetadata> = {
datos: ["item1", "item2", "item3"],
metadata: {
pagina: 1,
total: 100,
porPagina: 10
},
timestamp: Date.now()
};
Funciones de orden superior con múltiples genéricos
Los múltiples genéricos son especialmente útiles en funciones de orden superior:
function componer<A, B, C>(
f: (b: B) => C,
g: (a: A) => B
): (a: A) => C {
return (a) => f(g(a));
}
const convertirANumero = (str: string): number => parseInt(str, 10);
const duplicar = (num: number): number => num * 2;
const convertirYDuplicar = componer(duplicar, convertirANumero);
const resultado = convertirYDuplicar("10"); // 20
Promesas con múltiples genéricos
Cuando trabajamos con operaciones asíncronas, los múltiples genéricos nos permiten tipar correctamente diferentes escenarios:
interface ResultadoAsincrono<T, E = Error> {
datos?: T;
error?: E;
estado: 'pendiente' | 'completado' | 'error';
}
async function obtenerDatos<T, E>(
url: string
): Promise<ResultadoAsincrono<T, E>> {
try {
const respuesta = await fetch(url);
if (!respuesta.ok) {
throw await respuesta.json();
}
const datos = await respuesta.json();
return { datos, estado: 'completado' };
} catch (error) {
return {
error: error as E,
estado: 'error'
};
}
}
interface Usuario {
id: number;
nombre: string;
}
interface ErrorApi {
codigo: number;
mensaje: string;
}
// Uso con tipos específicos
const resultado = await obtenerDatos<Usuario, ErrorApi>('/api/usuarios/1');
if (resultado.estado === 'completado' && resultado.datos) {
console.log(`Usuario: ${resultado.datos.nombre}`);
} else if (resultado.error) {
console.error(`Error ${resultado.error.codigo}: ${resultado.error.mensaje}`);
}
Convenciones de nomenclatura
Cuando trabajamos con múltiples genéricos, es importante usar nombres descriptivos para mejorar la legibilidad:
// Convenciones comunes para múltiples genéricos
function procesar<TInput, TOutput>(entrada: TInput, fn: (data: TInput) => TOutput): TOutput {
return fn(entrada);
}
// Para pares clave-valor
interface Mapa<K, V> {
obtener(clave: K): V | undefined;
establecer(clave: K, valor: V): void;
}
// Para contenedores
class Contenedor<TContenido, TMetadata> {
contenido: TContenido;
metadata: TMetadata;
constructor(contenido: TContenido, metadata: TMetadata) {
this.contenido = contenido;
this.metadata = metadata;
}
}
Restricciones con múltiples genéricos
Podemos aplicar restricciones diferentes a cada parámetro de tipo:
function fusionar<
T extends object,
U extends { id: number }
>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const resultado = fusionar(
{ nombre: "Producto A" },
{ id: 123, precio: 29.99 }
);
// resultado: { nombre: string; id: number; precio: number; }
También podemos crear restricciones donde un parámetro genérico dependa de otro:
function seleccionarPropiedades<
T extends object,
K extends keyof T
>(obj: T, propiedades: K[]): Pick<T, K> {
const resultado = {} as Pick<T, K>;
propiedades.forEach(prop => {
resultado[prop] = obj[prop];
});
return resultado;
}
const usuario = {
id: 1,
nombre: "Elena",
email: "elena@ejemplo.com",
rol: "admin"
};
const credenciales = seleccionarPropiedades(usuario, ["id", "email"]);
// credenciales: { id: number; email: string; }
Buenas prácticas
Al trabajar con múltiples genéricos, es recomendable seguir estas pautas:
Limitar el número de parámetros: Usar demasiados parámetros genéricos puede hacer que el código sea difícil de entender. Intenta no exceder de 2-3 parámetros.
Nombres descriptivos: Usa nombres que reflejen el propósito de cada parámetro genérico (
TInput
,TOutput
,TKey
,TValue
, etc.).Valores por defecto: Considera proporcionar valores por defecto para parámetros genéricos que tengan casos de uso comunes.
Documentación: Documenta claramente el propósito de cada parámetro genérico, especialmente en APIs públicas.
Los múltiples genéricos son una herramienta fundamental para crear código TypeScript flexible y reutilizable que puede adaptarse a diferentes contextos mientras mantiene la seguridad de tipos, lo que resulta en aplicaciones más robustas y mantenibles.
Otros ejercicios de programación de TypeScript
Evalúa tus conocimientos de esta lección Tipos genéricos básicos con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Funciones
Reto composición de funciones
Reto tipos especiales
Reto tipos genéricos
Módulos
Polimorfismo
Funciones TypeScript
Interfaces
Funciones puras
Reto namespaces
Funciones flecha
Polimorfismo
Operadores
Conversor de unidades
Funciones flecha
Control de flujo
Herencia
Clases
Proyecto validación de tipado
Clases y objetos
Encapsulación
Herencia
Proyecto sistema de votación
Reto genéricos con clases
Inmutabilidad
Interfaces
Funciones de alto orden
Reto map y filter
Control de flujo
Interfaces
Reto funciones orden superior
Herencia y clases abstractas
Reto tipos mapped
Herencia de clases
Reto funciones puras
Variables y constantes
Introducción a TypeScript
Reto testing unitario
Funciones de primera clase
Clases
OOP y CRUD en TypeScript
Interfaces y su implementación
Tipos genéricos
Namespaces
Operadores y expresiones
Proyecto generador de contraseñas
Reto unión e intersección
Encapsulación
Tipos de unión e intersección
Tipos de unión e intersección
Reto hola mundo en TS
Variables y constantes
Funciones puras
Control de flujo
Introducción a TypeScript
Resolución de módulos
Control de flujo
Reto tipos de utilidad
Reto tipos literales y condicionales
Reto exportar e importar
Propiedades y métodos
Tipos de utilidad
Clases y objetos
Tipos de datos, variables y constantes
Proyecto Minigestor de tareas
Operadores
Funciones flecha y contexto
Proyecto Inventario de productos
Funciones
Reto type aliases
Funciones de alto orden
Funciones y parámetros tipados
Tipos literales
Reto enums
Tipos de utilidad
Modificadores de acceso y encapsulación
Polimorfismo
Tipos genéricos
Reto módulos
Tipos literales
Inmutabilidad
Proyecto Generator de datos
Variables y constantes
Funciones de primera clase
Todas las lecciones de TypeScript
Accede a todas las lecciones de TypeScript y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Typescript
Introducción Y Entorno
Instalación Y Configuración De Typescript
Introducción Y Entorno
Tipos De Datos, Variables Y Constantes
Sintaxis
Operadores Y Expresiones
Sintaxis
Control De Flujo
Sintaxis
Funciones Y Parámetros Tipados
Sintaxis
Funciones Flecha Y Contexto
Sintaxis
Enums
Sintaxis
Type Aliases Y Aserciones De Tipo
Sintaxis
Clases Y Objetos
Programación Orientada A Objetos
Interfaces Y Su Implementación
Programación Orientada A Objetos
Modificadores De Acceso Y Encapsulación
Programación Orientada A Objetos
Herencia Y Clases Abstractas
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Decoradores Básicos
Programación Orientada A Objetos
Propiedades Y Métodos
Programación Orientada A Objetos
Inmutabilidad
Programación Funcional
Funciones Puras Y Efectos Secundarios
Programación Funcional
Funciones De Primera Clase
Programación Funcional
Funciones De Alto Orden
Programación Funcional
Conceptos Básicos E Inmutabilidad
Programación Funcional
Funciones De Primera Clase Y Orden Superior
Programación Funcional
Composición De Funciones
Programación Funcional
Métodos Funcionales De Arrays (Map, Filter, Reduce)
Programación Funcional
Tipos Literales Y Tipos Condicionales
Tipos Intermedios Y Avanzados
Tipos Genéricos Básicos
Tipos Intermedios Y Avanzados
Tipos De Unión E Intersección
Tipos Intermedios Y Avanzados
Tipos De Utilidad (Partial, Required, Pick, Etc)
Tipos Intermedios Y Avanzados
Unknown, Never Y Tipos Especiales
Tipos Intermedios Y Avanzados
Tipos Mapped
Tipos Intermedios Y Avanzados
Genéricos Con Clases E Interfaces
Tipos Intermedios Y Avanzados
Módulos
Namespaces Y Módulos
Namespaces
Namespaces Y Módulos
Resolución De Módulos
Namespaces Y Módulos
Exportación E Importación De Módulos
Namespaces Y Módulos
Introducción A Módulos
Namespaces Y Módulos
Testing Unitario En Typescript
Testing
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la sintaxis básica y el uso de tipos genéricos en funciones, interfaces y clases.
- Aplicar funciones genéricas para mantener la seguridad de tipos y reutilización de código.
- Implementar restricciones con extends para limitar los tipos genéricos permitidos.
- Utilizar múltiples parámetros genéricos para crear estructuras y funciones más flexibles.
- Seguir buenas prácticas y convenciones para mejorar la legibilidad y mantenimiento del código genérico.