Repaso de tipos básicos y complejos en TypeScript. Qué son los genéricos y la conversión de tipos.
En TypeScript, al igual que en JavaScript, existen tipos primitivos que representan datos simples y básicos. Veamos los tipos primitivos principales en TypeScript con ejemplos de código:
Representa valores numéricos, ya sean enteros o decimales.
let edad: number = 25let precio: number = 99.99
// Ejemplo de operaciones con númeroslet suma: number = edad + precio
Son valores de texto o cadenas de caracteres (strings en inglés).
let nombre: string = 'Juan'let mensaje: string = `Hola, ${nombre}!`
// Ejemplo de longitud de cadenalet longitud: number = mensaje.length
Representa valores lógicos, verdadero true o falso false.
let activo: boolean = truelet esMayorDeEdad: boolean = edad >= 18
// Ejemplo de expresiones lógicaslet puedeVotar: boolean = esMayorDeEdad && activo
Son tipos que tienen valores null (un valor asignado) y undefined (variable declarada pero si definir ningún valor).
let valorNulo: null = nulllet valorNoDefinido: undefined = undefined
Representa valores únicos e inmutables utilizados como identificadores de propiedad en objetos. Introducidos en ECMAScript 2015 (ES6).
const simbolo1: symbol = Symbol('id')const simbolo2 = Symbol('id') // Los símbolos son siempre únicos
// Ejemplo de uso de símbolos en objetosconst persona = { nombre: 'Ana', [simbolo1]: 28, // Uso de un símbolo como clave de propiedad}
Representa valores enteros mayores que 2^53 - 1 o menores que -(2^53 - 1).
const valorGrande: bigint = 9007199254740991nconst otroValor: bigint = BigInt(100) // Creación de un valor BigInt
// Ejemplo de operaciones con bigintsconst sumaBigInt: bigint = valorGrande + otroValor
Estos son algunos de los tipos primitivos básicos en TypeScript que se utilizan para definir variables y estructuras de datos simples en el código.
# Tipos Complejos en TypeScript
Los tipos complejos en TypeScript permiten representar y trabajar con estructuras de datos más sofisticadas y flexibles, lo que proporciona un mayor nivel de seguridad y claridad en el desarrollo de aplicaciones. Aquí te muestro algunos de los tipos complejos principales con ejemplos de código:
Representa una colección ordenada de elementos del mismo tipo.
let numeros: number[] = [1, 2, 3, 4, 5]let palabras: string[] = ['Hola', 'Mundo']
// Uso de arrays con tipos múltiples (unión de tipos)let variedad: (number | string)[] = [10, 'veinte', 30]
Una tupla es un array de longitud fija que permite definir tipos específicos para cada posición.
let tupla: [string, number, boolean] = ['Hola', 10, true]
// Acceso a elementos de la tuplalet mensaje: string = tupla[0]let valor: number = tupla[1]let activo: boolean = tupla[2]
Representa un objeto genérico con propiedades y valores.
let persona: { nombre: string; edad: number } = { nombre: 'Ana', edad: 30,}
// Acceso a las propiedades del objetoconsole.log(persona.nombre)console.log(persona.edad)
Representa un tipo dinámico que puede tomar cualquier valor.
let variableDinamica: any = 10variableDinamica = 'Hola'variableDinamica = true
Permite definir variables que pueden contener más de un tipo.
let resultado: number | stringresultado = 100resultado = 'Éxito'
Permite definir un conjunto de constantes nombradas.
enum DiaSemana { Lunes, Martes, Miércoles, Jueves, Viernes,}
let diaLaboral: DiaSemana = DiaSemana.Lunesconsole.log(diaLaboral) // Imprimirá: 0 (valor asignado a Lunes)
En TypeScript, tanto type como interface son herramientas que permiten definir estructuras de datos personalizadas. Aunque pueden parecer similares en algunos aspectos, tienen diferencias en su funcionamiento y uso. Veamos cada uno de ellos:
type es una forma de definir un alias para un tipo existente o crear un nuevo tipo a partir de una combinación de otros tipos. Esto permite reutilizar tipos existentes o crear nuevos tipos basados en composiciones, uniones, intersecciones u otras operaciones con tipos.
Ejemplo:
type Coordenada = { x: number y: number}
type Punto = Coordenada & { color: string }
let punto: Punto = { x: 10, y: 20, color: 'rojo' }
En el ejemplo anterior, Punto
es un tipo que combina las propiedades de Coordenada
y agrega una propiedad adicional color
. Los tipos pueden ser usados para definir tipos más complejos y reutilizables.
interface, por otro lado, se utiliza principalmente para definir la forma de una estructura de datos. Es una forma de declarar contratos que los objetos deben cumplir. Las interfaces son más adecuadas para definir la forma de un objeto y permiten la herencia entre interfaces para extender su comportamiento.
Ejemplo:
interface Coordenada { x: number y: number}
interface Punto extends Coordenada { color: string}
let punto: Punto = { x: 10, y: 20, color: 'rojo' }
En este ejemplo, Punto
es una interfaz que extiende la interfaz Coordenada
, agregando la propiedad color
. Las interfaces también pueden ser implementadas por clases para garantizar que la clase cumpla con la estructura definida por la interfaz.
En la práctica, la elección entre type e interface depende del escenario particular y las necesidades del desarrollo. A menudo, ambas pueden ser utilizadas en conjunto para beneficiarse de las diferentes características que ofrecen. Es importante entender sus diferencias y utilizarlas de manera efectiva.
Los genéricos (generics) es un término en TypeScript que sirve para describir la característica que permite escribir código flexible y reutilizable al trabajar con tipos de datos. Permiten la creación de componentes que pueden funcionar con una variedad de tipos sin perder la información de tipo durante la ejecución.
Los genéricos se definen utilizando el símbolo <T>
, donde T
es una variable de tipo. Estos tipos pueden ser utilizados dentro de funciones, clases y otros constructores para proporcionar flexibilidad en la elección de tipos. Por ejemplo, la función identidad soporta varios tipos:
function identidad<T>(valor: T): T { return valor}
let numero: number = identidad(5) // T se infiere como numberlet palabra: string = identidad('Hola') // T se infiere como string
Puedes utilizar varias variables de tipo genérico en una función para trabajar con múltiples tipos.
function imprimirPar<T, U>(a: T, b: U): void { console.log(`${a}, ${b}`)}
imprimirPar(1, 'Dos') // Salida: 1, DosimprimirPar('Tres', 4) // Salida: Tres, 4
Las clases también pueden hacer uso de generics para proporcionar flexibilidad en la elección de tipos.
class Contenedor<T> { valor: T
constructor(valor: T) { this.valor = valor }}
let contenedorNumero = new Contenedor<number>(5)let contenedorString = new Contenedor<string>('Hola')
Una clase puede tener múltiples variables de tipo genérico.
class ParOrdenado<T, U> { constructor( public primero: T, public segundo: U ) {}}
let par = new ParOrdenado<number, string>(1, 'Dos')console.log(par.primero) // Salida: 1console.log(par.segundo) // Salida: Dos
Las Type assertions o conversión de tipos, también conocidas como “casting”, son una característica presente en muchos lenguajes de programación como Java o C#. También está presente en TypeScript. Básicamente sirve para ayudar al compilador a determinar un tipo específico a un valor cuando este no puede determinarlo por si mismo.
La sintaxis básica de una Type Assertion es utilizar el operador as
o la notación <>
. Aquí hay ejemplos de ambas formas:
// Sintaxis con <>let longitud: number = (<string>miVariable).length
// Sintaxis con 'as'let longitud: number = (miVariable as string).length
El uso de Type assertions se vuelve útil en situaciones como:
let miVariable: any = 'Hola, Mundo!'let longitud: number = (miVariable as string).length
En este caso, miVariable
se declara como tipo any
, pero el desarrollador sabe que, en este contexto específico, su valor será una cadena de texto (string). La Type Assertion le permite a TypeScript tratar miVariable
como una cadena, permitiendo el acceso a la propiedad length
.
Aunque las Type assertions pueden ser útiles en ciertos escenarios, es importante usarlas con precaución para evitar situaciones de ejecución impredecibles. Veamos las mejores prácticas:
Utilízalas sólo cuando sea necesario
Evita Type assertions con any
any
, ya que esto anula gran parte de la seguridad de tipo proporcionada por TypeScript.Usa Type Guards cuando sea posible
// Ejemplo de Type Guardfunction esCadena(valor: any): valor is string { return typeof valor === 'string'}
let miVariable: any = 'Hola, Mundo!'if (esCadena(miVariable)) { let longitud: number = miVariable.length // TypeScript ahora entiende que es una cadena}
Las Type assertions pueden ser valiosas en ciertos escenarios, pero es crucial utilizarlas con responsabilidad y considerar alternativas más seguras cuando sea posible. Esto ayuda a mantener la integridad del sistema de tipos de TypeScript y a prevenir errores difíciles de depurar.
En este artículo hemos visto los tipos básicos de TypeScript, los tipos complejos, qué son los genéricos y la conversión de tipos. Comprender todos estos términos no solo nos ayudará a mejorar la calidad del código, sino que también nos abre la puerta a prácticas de desarrollo más avanzadas y eficientes para el desarrollo de nuestras aplicaciones con TypeScript.
Si quieres profundizar más sobre tipos puedes visitar la documentacion oficial.