Explora funciones avanzadas de TypeScript como los tipos literales de plantilla y los tipos condicionales para escribir c贸digo m谩s expresivo y mantenible. Domina la manipulaci贸n de tipos para escenarios complejos.
Tipos avanzados de TypeScript: Dominando los tipos literales de plantilla y los tipos condicionales
La fortaleza de TypeScript reside en su potente sistema de tipos. Si bien los tipos b谩sicos como string, number y boolean son suficientes para muchos escenarios, las funciones avanzadas como los tipos literales de plantilla y los tipos condicionales desbloquean un nuevo nivel de expresividad y seguridad de tipos. Esta gu铆a proporciona una descripci贸n completa de estos tipos avanzados, explorando sus capacidades y demostrando aplicaciones pr谩cticas.
Entendiendo los tipos literales de plantilla
Los tipos literales de plantilla se basan en los literales de plantilla de JavaScript, lo que le permite definir tipos basados en la interpolaci贸n de cadenas. Esto permite la creaci贸n de tipos que representan patrones de cadena espec铆ficos, lo que hace que su c贸digo sea m谩s robusto y predecible.
Sintaxis b谩sica y uso
Los tipos literales de plantilla utilizan acentos graves (`) para encerrar la definici贸n del tipo, de forma similar a los literales de plantilla de JavaScript. Dentro de los acentos graves, puede interpolar otros tipos utilizando la sintaxis ${}. Aqu铆 es donde ocurre la magia: esencialmente, est谩 creando un tipo que es una cadena, construido en tiempo de compilaci贸n en funci贸n de los tipos dentro de la interpolaci贸n.
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIEndpoint = `/api/${string}`;
// Ejemplo de uso
const getEndpoint: APIEndpoint = "/api/users"; // V谩lido
const postEndpoint: APIEndpoint = "/api/products/123"; // V谩lido
const invalidEndpoint: APIEndpoint = "/admin/settings"; // TypeScript no mostrar谩 un error aqu铆, ya que `string` puede ser cualquier cosa
En este ejemplo, APIEndpoint es un tipo que representa cualquier cadena que comience con /api/. Si bien este ejemplo b谩sico es 煤til, el verdadero poder de los tipos literales de plantilla surge cuando se combinan con restricciones de tipo m谩s espec铆ficas.
Combinando con tipos de uni贸n
Los tipos literales de plantilla realmente brillan cuando se usan con tipos de uni贸n. Esto le permite crear tipos que representan un conjunto espec铆fico de combinaciones de cadenas.
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIPath = "users" | "products" | "orders";
type APIEndpoint = `/${APIPath}/${HTTPMethod}`;
// Puntos finales de API v谩lidos
const getUsers: APIEndpoint = "/users/GET";
const postProducts: APIEndpoint = "/products/POST";
// Puntos finales de API no v谩lidos (resultar谩n en errores de TypeScript)
// const invalidEndpoint: APIEndpoint = "/users/PATCH"; // Error: "/users/PATCH" no es asignable al tipo "/users/GET" | "/users/POST" | "/users/PUT" | "/users/DELETE" | "/products/GET" | "/products/POST" | ... 3 m谩s ... | "/orders/DELETE".
Ahora, APIEndpoint es un tipo m谩s restrictivo que solo permite combinaciones espec铆ficas de rutas de API y m茅todos HTTP. TypeScript marcar谩 cualquier intento de usar combinaciones no v谩lidas, lo que mejora la seguridad de los tipos.
Manipulaci贸n de cadenas con tipos literales de plantilla
TypeScript proporciona tipos intr铆nsecos de manipulaci贸n de cadenas que funcionan a la perfecci贸n con los tipos literales de plantilla. Estos tipos le permiten transformar cadenas en tiempo de compilaci贸n.
- May煤sculas: Convierte una cadena a may煤sculas.
- Min煤sculas: Convierte una cadena a min煤sculas.
- Capitalizar: Pone en may煤scula la primera letra de una cadena.
- Descapitalizar: Pone en min煤scula la primera letra de una cadena.
type Greeting = "hello world";
type UppercaseGreeting = Uppercase; // "HELLO WORLD"
type LowercaseGreeting = Lowercase; // "hello world"
type CapitalizedGreeting = Capitalize; // "Hello world"
type UncapitalizedGreeting = Uncapitalize; // "hello world"
Estos tipos de manipulaci贸n de cadenas son particularmente 煤tiles para generar autom谩ticamente tipos basados en convenciones de nomenclatura. Por ejemplo, podr铆a derivar tipos de acci贸n de nombres de eventos o viceversa.
Aplicaciones pr谩cticas de los tipos literales de plantilla
- Definici贸n de punto final de API: Como se demostr贸 anteriormente, la definici贸n de puntos finales de API con restricciones de tipo precisas.
- Manejo de eventos: Creaci贸n de tipos para nombres de eventos con prefijos y sufijos espec铆ficos.
- Generaci贸n de clases CSS: Generaci贸n de nombres de clases CSS basados en nombres y estados de componentes.
- Creaci贸n de consultas de bases de datos: Garantizar la seguridad de los tipos al construir consultas de bases de datos.
Ejemplo internacional: Formato de moneda
Imagine que est谩 creando una aplicaci贸n financiera que admite varias divisas. Puede usar tipos literales de plantilla para aplicar el formato de moneda correcto.
type CurrencyCode = "USD" | "EUR" | "GBP" | "JPY";
type CurrencyFormat = `${number} ${T}`;
const priceUSD: CurrencyFormat<"USD"> = "100 USD"; // V谩lido
const priceEUR: CurrencyFormat<"EUR"> = "50 EUR"; // V谩lido
// const priceInvalid: CurrencyFormat<"USD"> = "100 EUR"; // Error: Type 'string' is not assignable to type '`${number} USD`'.
function formatCurrency(amount: number, currency: T): CurrencyFormat {
return `${amount} ${currency}`;
}
const formattedUSD = formatCurrency(250, "USD"); // Type: "250 USD"
const formattedEUR = formatCurrency(100, "EUR"); // Type: "100 EUR"
Este ejemplo garantiza que los valores de las divisas siempre tengan el formato correcto con el c贸digo de divisa correcto, lo que evita posibles errores.
Profundizando en los tipos condicionales
Los tipos condicionales introducen l贸gica de ramificaci贸n en el sistema de tipos de TypeScript, lo que le permite definir tipos que dependen de otros tipos. Esta caracter铆stica es incre铆blemente poderosa para crear definiciones de tipos altamente flexibles y reutilizables.
Sintaxis b谩sica y uso
Los tipos condicionales utilizan la palabra clave infer y el operador ternario (condition ? trueType : falseType) para definir condiciones de tipo.
type IsString = T extends string ? true : false;
type StringCheck = IsString; // type StringCheck = true
type NumberCheck = IsString; // type NumberCheck = false
En este ejemplo, IsString es un tipo condicional que verifica si T es asignable a string. Si lo es, el tipo se resuelve a true; de lo contrario, se resuelve a false.
La palabra clave infer
La palabra clave infer le permite extraer un tipo de un tipo. Esto es particularmente 煤til cuando se trabaja con tipos complejos como tipos de funci贸n o tipos de matriz.
type ReturnType any> = T extends (...args: any) => infer R ? R : any;
function add(a: number, b: number): number {
return a + b;
}
type AddReturnType = ReturnType; // type AddReturnType = number
En este ejemplo, ReturnType extrae el tipo de retorno de un tipo de funci贸n T. La parte infer R del tipo condicional infiere el tipo de retorno y lo asigna a la variable de tipo R. Si T no es un tipo de funci贸n, el tipo se resuelve a any.
Tipos condicionales distributivos
Los tipos condicionales se vuelven distributivos cuando el tipo comprobado es un par谩metro de tipo desnudo. Esto significa que el tipo condicional se aplica a cada miembro del tipo de uni贸n por separado.
type ToArray = T extends any ? T[] : never;
type NumberOrStringArray = ToArray; // type NumberOrStringArray = string[] | number[]
En este ejemplo, ToArray convierte un tipo T en un tipo de matriz. Debido a que T es un par谩metro de tipo desnudo (no envuelto en otro tipo), el tipo condicional se aplica a number y string por separado, lo que da como resultado una uni贸n de number[] y string[].
Aplicaciones pr谩cticas de los tipos condicionales
- Extracci贸n de tipos de retorno: Como se demostr贸 anteriormente, la extracci贸n del tipo de retorno de una funci贸n.
- Filtrado de tipos de una uni贸n: Creaci贸n de un tipo que contiene solo tipos espec铆ficos de una uni贸n.
- Definici贸n de tipos de funci贸n sobrecargados: Creaci贸n de diferentes tipos de funci贸n basados en los tipos de entrada.
- Creaci贸n de guardias de tipo: Definici贸n de funciones que restringen el tipo de una variable.
Ejemplo internacional: Manejo de diferentes formatos de fecha
Diferentes regiones del mundo utilizan diferentes formatos de fecha. Puede usar tipos condicionales para manejar estas variaciones.
type DateFormat = "YYYY-MM-DD" | "MM/DD/YYYY" | "DD.MM.YYYY";
type ParseDate = T extends "YYYY-MM-DD"
? { year: number; month: number; day: number; format: "YYYY-MM-DD" }
: T extends "MM/DD/YYYY"
? { month: number; day: number; year: number; format: "MM/DD/YYYY" }
: T extends "DD.MM.YYYY"
? { day: number; month: number; year: number; format: "DD.MM.YYYY" }
: never;
function parseDate(dateString: string, format: T): ParseDate {
// (La implementaci贸n manejar铆a diferentes formatos de fecha)
if (format === "YYYY-MM-DD") {
const [year, month, day] = dateString.split("-").map(Number);
return { year, month, day, format } as ParseDate;
} else if (format === "MM/DD/YYYY") {
const [month, day, year] = dateString.split("/").map(Number);
return { month, day, year, format } as ParseDate;
} else if (format === "DD.MM.YYYY") {
const [day, month, year] = dateString.split(".").map(Number);
return { day, month, year, format } as ParseDate;
} else {
throw new Error("Formato de fecha no v谩lido");
}
}
const parsedDateISO = parseDate("2023-10-27", "YYYY-MM-DD"); // Type: { year: number; month: number; day: number; format: "YYYY-MM-DD"; }
const parsedDateUS = parseDate("10/27/2023", "MM/DD/YYYY"); // Type: { month: number; day: number; year: number; format: "MM/DD/YYYY"; }
const parsedDateEU = parseDate("27.10.2023", "DD.MM.YYYY"); // Type: { day: number; month: number; year: number; format: "DD.MM.YYYY"; }
console.log(parsedDateISO.year); // Accede al a帽o sabiendo que estar谩 ah铆
Este ejemplo utiliza tipos condicionales para definir diferentes funciones de an谩lisis de fechas basadas en el formato de fecha especificado. El tipo ParseDate garantiza que el objeto devuelto tenga las propiedades correctas seg煤n el formato.
Combinando tipos literales de plantilla y tipos condicionales
El verdadero poder llega cuando combina tipos literales de plantilla y tipos condicionales. Esto permite manipulaciones de tipos incre铆blemente poderosas.
type EventName = `on${Capitalize}`;
type ExtractEventPayload = T extends EventName
? { type: T; payload: any } // Simplificado para demostraci贸n
: never;
type ClickEvent = EventName<"click">; // "onClick"
type MouseOverEvent = EventName<"mouseOver">; // "onMouseOver"
//Ejemplo de funci贸n que toma un tipo
function processEvent(event: T): ExtractEventPayload {
//En una implementaci贸n real, en realidad despachar铆amos el evento.
console.log(`Procesando evento ${event}`);
//En una implementaci贸n real, la carga 煤til se basar铆a en el tipo de evento.
return { type: event, payload: {} } as ExtractEventPayload;
}
//Tenga en cuenta que los tipos de retorno son muy espec铆ficos:
const clickEvent = processEvent("onClick"); // { type: "onClick"; payload: any; }
const mouseOverEvent = processEvent("onMouseOver"); // { type: "onMouseOver"; payload: any; }
//Si usa otras cadenas, obtiene nunca:
// const someOtherEvent = processEvent("someOtherEvent"); // El tipo es `never`
Mejores pr谩cticas y consideraciones
- Mantenlo simple: Si bien son poderosos, estos tipos avanzados pueden volverse complejos r谩pidamente. Esfu茅rcese por la claridad y el mantenimiento.
- Pruebe a fondo: Aseg煤rese de que sus definiciones de tipos se comporten como se espera escribiendo pruebas unitarias completas.
- Documente su c贸digo: Documente claramente el prop贸sito y el comportamiento de sus tipos avanzados para mejorar la legibilidad del c贸digo.
- Considere el rendimiento: El uso excesivo de tipos avanzados puede afectar el tiempo de compilaci贸n. Perfile su c贸digo y optimice donde sea necesario.
Conclusi贸n
Los tipos literales de plantilla y los tipos condicionales son herramientas poderosas en el arsenal de TypeScript. Al dominar estos tipos avanzados, puede escribir un c贸digo m谩s expresivo, mantenible y con seguridad de tipos. Estas funciones le permiten capturar relaciones complejas entre tipos, aplicar restricciones m谩s estrictas y crear definiciones de tipos altamente reutilizables. Adopte estas t茅cnicas para elevar sus habilidades de TypeScript y crear aplicaciones robustas y escalables para una audiencia global.