¡Bienvenido a esta aventura por el maravilloso mundo del TypeScript! Estoy súper emocionado de que estés aquí conmigo, porque hoy vamos a construir juntos un proyecto y, durante esta jornada, desentrañaremos los conceptos más importantes que TypeScript tiene para ofrecer.
Lo sé, aprender una nueva tecnología puede parecer un poco intimidante al principio, ¡pero no te preocupes! Estoy aquí para guiarte paso a paso, aclarar tus dudas y garantizar que esta experiencia sea divertida y enriquecedora. Y, claro, estamos aquí para aprender juntos, así que ¡cualquier caída es solo una oportunidad de crecimiento!
Vamos a crear una pequeña aplicación de Lista de Compras utilizando TypeScript y, mientras desarrollamos, te mostraré cómo este increíble lenguaje puede ayudarte a maximizar tu productividad y reducir errores inesperados. ¿Adivina qué? ¡Haremos todo esto en un entorno Vanilla! Sí, leíste bien, sin frameworks complejos, solo el bueno y viejo JavaScript, con un ¡toque especial de TypeScript!
Entonces, ¿preparado para embarcar en esta jornada junto a mí? Vamos a aprender, codificar, enfrentar algunos desafíos, y lo más importante, ¡divertirnos en este proceso! Toma tu taza de café (o té, si prefieres), ajusta tu IDE y ¡vamos a comenzar este increíble viaje por TypeScript!
¡Qué sensación increíble empezar algo nuevo, ¿verdad? ¡Siento las mismas mariposas en mi estómago que tú! Así que, relájate, respira hondo y vamos a empezar por configurar nuestro ambiente de desarrollo.
Primero, vamos a aclarar una duda que puede estar pasando por tu cabeza: "¿Qué es Vanilla en este contexto?". Cuando hablamos de Vanilla en desarrollo web, nos referimos a JavaScript puro, sin frameworks o bibliotecas externas. Es lo básico de lo básico, el ingrediente en su forma más simple y pura, por eso el nombre "Vanilla".
Para empezar, necesitas tener Node.js y NPM (Node Package Manager) descargados en tu máquina. Ellos serán nuestros fieles escuderos, ayudando a gestionar paquetes y a ejecutar nuestro proyecto. Si no los tienes descargados, te dejo este artículo que te brindará el paso a paso.
Con Node.js y NPM listos, abre tu terminal y crea un nuevo directorio para nuestro proyecto. Siéntete como en casa, elige un lugar que te guste en tu computadora e inicializa un nuevo proyecto Node.js con el comando npm init -y
.
¡Estamos bien! Ahora, el siguiente paso es instalar TypeScript. Lo utilizaremos bastante, así que escribe npm install -g typescript
para descargarlo en tu máquina.
¡Ya estás casi listo! Dentro del directorio raíz del proyecto, crea un archivo llamado tsconfig.json
. Este archivo es la fuente de verdad para la configuración de cómo queremos que TypeScript se comporte en relación a nuestro proyecto. Nos permite activar y desactivar funcionalidades. Empecemos con algunas pocas configuraciones:
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs",
"rootDir": "./",
"outDir": "./dist",
"lib": ["es2015", "dom"], // Incluye las bibliotecas necesarias para ES2015 y DOM
},
"include": ["./**/*"], // Incluir todos los archivos en el directorio actual y subdirectorios
"exclude": ["node_modules", "dist"] // Excluir "node_modules" y "dist"
}
¿Vamos a revisar un poco qué hace cada configuración?
target
: "es2015"
: Define la versión de ECMAScript que el código compilado seguirá. En este caso, se elige "es2015" para aprovechar las nuevas características de ES6.module
: "commonjs"
: Especifica el sistema de módulos que se usará. "commonjs" es ideal para proyectos Node.js, ya que es ampliamente soportado y estándar en ese entorno.rootDir
: "./"
: Indica el directorio raíz donde se encuentran los archivos TypeScript. Es el punto de partida para la compilación.outDir
: "./dist"
: Especifica el directorio donde TypeScript guardará los archivos JavaScript compilados. Mantiene el código compilado separado del código fuente.lib
: ["es2015", "dom"]
: Incluye las bibliotecas necesarias para el soporte de ES2015 y del DOM, lo que es útil si el proyecto interactúa con el navegador.include
: ["./**/*"]
: Incluye todos los archivos dentro del directorio actual y sus subdirectorios en el proceso de compilación.exclude
: ["node_modules", "dist"]
: Excluye los directorios "node_modules" y "dist" del proceso de compilación, para evitar incluir dependencias externas o archivos compilados anteriores.Si aún no conoces todos estos conceptos de NPM y paquetes, y quieres profundizar más en la teoría detrás de la práctica, te invito a ver nuestro AluraTips con el instructor Leonardo Castillo.
Repara que, para seguir codificando aquí, no es estrictamente necesario tener toda esta bagaje teórica. ¡Siéntete libre de elegir lo que mejor se adapte a tu momento!
Vamos a comenzar creando nuestro archivo index.html
. Este archivo será la puerta de entrada de nuestra aplicación. Para dar un toque especial a la interfaz, vamos a utilizar Bootstrap. Si no estás familiarizado con ello, es un framework de CSS que nos ayuda a estilizar nuestra aplicación de forma más eficiente. Si quieres profundizar más sobre Bootstrap, te sugiero este artículo.
De hecho, probablemente siempre sugeriré contenido adicional para temas que exploraremos en profundidad aquí, ¿vale?
Ahora, agrega el CSS de Bootstrap en nuestro HTML así:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Lista de Compras con TypeScript</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h1>Lista de Compras con TypeScript</h1>
<form id="formularioItem">
<div class="form-group">
<label for="item">Artículo:</label>
<input type="text" class="form-control" id="item" required>
</div>
<button type="submit" class="btn btn-primary">Agregar</button>
</form>
<ul id="listaItems" class="list-group mt-3">
<!-- Los artículos de la lista serán insertados aquí -->
</ul>
</div>
<script src="app.js"></script>
</body>
</html>
Ahora, probablemente te estés preguntando: "¿Por qué no empezamos a escribir el CRUD directamente dentro de una etiqueta <script>
?" Bueno, ¡aquí viene una peculiaridad de TypeScript! Vamos a crear un archivo .ts
separado porque, a diferencia del JavaScript, TypeScript necesita ser "compilado".
Vamos a crear ahora un archivo llamado app.ts
. Finalmente, podemos comenzar a escribir nuestra aplicación. Empecemos con la base de nuestra aplicación. Necesitamos definir cómo es un ítem de la lista de compras, qué propiedades debería tener y cuál es el tipo de cada una de estas propiedades. En TypeScript, una interfaz es una forma poderosa de definir contratos dentro de tu código y con el código fuera de tu proyecto. En nuestro caso, la interfaz Item
se define así:
interface Item{
id: string;
nombre: string;
}
Esta interfaz está describiendo la forma que un objeto Item debe tener. En este caso, todo ítem debe tener un id
y un nombre
, ambos del tipo string
. Al usar esta interface
, estamos básicamente diciendo a TypeScript: "¡Oye, queremos objetos que tengan esta forma específica!". Esto nos proporciona más seguridad, porque TypeScript generará un error si intentamos crear un ítem que no se adhiera a esta forma.
Para interactuar con el DOM, necesitamos seleccionar los elementos con los cuales deseamos trabajar. Sin embargo, TypeScript no conoce el tipo de elemento que document.getElementById
retornará, por eso necesitamos usar el type casting para informar a TypeScript del tipo de elemento con el que estamos trabajando:
// Seleccionando elementos del DOM
const formularioItem = document.getElementById('formularioItem') as HTMLFormElement;
const listaItems = document.getElementById('listaItems') as HTMLUListElement;
const inputItem = document.getElementById('item') as HTMLInputElement;
En estos casos, estamos utilizando el type assertion para informar a TypeScript que formularioItem
es un elemento de formulario, listaItems
es una lista no ordenada y inputItem
es un elemento de entrada. Esto es importante porque ahora TypeScript sabe cuáles propiedades y métodos están disponibles para estos elementos, mejorando la autocompletación y detectando errores.
Debes estar preguntándote: ¿de dónde vienen estos tipos que usamos para el type assertion? Bien, ellos son parte de las definiciones de tipos del DOM (Document Object Model) proporcionadas por TypeScript. Cuando trabajas con TypeScript en el ambiente del navegador, el compilador utiliza un conjunto de definiciones de tipos llamado
lib.dom.d.ts
para comprender los tipos asociados al DOM. Estos tipos representan los elementos HTML y sus propiedades, métodos y eventos, y están derivados de las especificaciones web. Por lo tanto,HTMLFormElement
,HTMLUListElement
yHTMLInputElement
son tipos que representan, respectivamente, un elemento de formulario HTML, una lista no ordenada y un elemento de entrada. Utilizarlos proporciona autocompletado de código inteligente y verificaciones de tipo, facilitando la manipulación segura de los elementos del DOM.
Vamos ahora a crear una función que cargue nuestros ítems:
// Cargando ítems del localStorage
const cargarItems = (): Item[] => {
const items = localStorage.getItem('items');
return items ? JSON.parse(items) : [];
};
Creo que, aunque sea una función sencilla de 4 líneas, enseña bastante para nosotros
Item[]
: La función tiene un tipo de retorno explícito Item[]
, que indica que esta función retornará un array de objetos del tipo Item
. Tipar explícitamente la función es una práctica recomendada en TypeScript para garantizar la consistencia de los tipos de datos manipulados en la aplicación. Es decir, quien llame a esta función ya sabe lo que puede esperar de ella.const items = localStorage.getItem('items')
: busca en localStorage
una entrada con la llave 'items'. Si se encuentra, la entrada es asignada a la constante items
como una cadena JSON.JSON.parse
: La línea return items ? JSON.parse(items) : [];
usa un operador ternario. Si items
es verdadero (es decir, si la entrada 'items' existe en el localStorage
), la función retornará el valor de items
parseado de vuelta a un objeto JavaScript utilizando JSON.parse(items)
. De lo contrario, la función retornará un array vacío []
.Esta aproximación proporciona un manejo seguro y eficiente de datos, garantizando que la aplicación pueda lidiar adecuadamente con escenarios donde 'items' puede no estar presente en el localStorage
.
// Guardando elementos en el localStorage
const guardarElementos = (elementos: Item[]) => {
localStorage.setItem('elementos', JSON.stringify(elementos));
};
En esta función, estamos tratando el concepto de tipado implícito. Esto ocurre porque, aunque estamos especificando el tipo del parámetro de entrada items
como un array de Item
, no estamos anotando explícitamente el tipo de retorno de la función. TypeScript es lo suficientemente inteligente para inferir el tipo de retorno basado en lo que la función hace. En este caso, como la función no tiene una instrucción de retorno, TypeScript infiera que el tipo de retorno es void
, lo que significa que la función no retorna nada. También podemos hacerlo de forma explícita así:
// Guardando elementos en localStorage
const guardarElementos = (elementos: Elemento[]): void => {
localStorage.setItem('elementos', JSON.stringify(elementos));
};
¿Notaste la diferencia? Agrega : void
al final de la declaración de la función. Esta es una forma de decir explícitamente a TypeScript: "¡Oye, esta función no va a retornar nada!"
El tipo void se utiliza para representar la ausencia de un valor. Cuando decimos que una función es del tipo
void
, estamos afirmando que esta función no retorna ningún valor. En pocas palabras, es como si la función dijera: "¡Voy a realizar una tarea, pero no voy a devolver ningún resultado para ti!"
Ahora que ya tenemos una idea de cómo TypeScript maneja las cosas (ba dum tsss), vamos a escribir el resto de nuestra aplicación. Necesitamos los métodos:
agregarItem
removerItem
editarItem
renderizarItens
Además de eso, necesitaremos agregar un listener que escuche el evento de submit del formulario. ¿Qué tal si experimentas? Aún no hemos podido probar, pero puedes comparar el resultado con el mío y aprender de las diferencias. Si quieres, incluso puedes publicar tu versión. ¡Adelante! Y si lo haces, márcame en Instagram o en LinkedIn.
En mi versión completa del app.ts
quedó así:
// Definiendo la interfaz para el tipo Item
interface Item {
id: string;
nombre: string;
}
// Seleccionando elementos del DOM
const formularioItem = document.getElementById('formularioItem') as HTMLFormElement;
const listaItems = document.getElementById('listaItems') as HTMLUListElement;
const inputItem = document.getElementById('item') as HTMLInputElement;
// Cargando items desde localStorage
const cargarItems = (): Item[] => {
const items = localStorage.getItem('items');
return items ? JSON.parse(items) : [];
};
// Guardando items en localStorage
const guardarItems = (items: Item[]) => {
localStorage.setItem('items', JSON.stringify(items));
};
// Añadiendo un nuevo item
const agregarItem = (nombre: string) => {
const items = cargarItems();
const nuevoItem: Item = {
id: new Date().toISOString(),
nombre
};
items.push(nuevoItem);
guardarItems(items);
};
// Eliminando un item por ID
const eliminarItem = (id: string) => {
const items = cargarItems();
const itemsActualizados = items.filter(item => item.id !== id);
guardarItems(itemsActualizados);
};
// Editando un item por ID
const editarItem = (id: string, nuevoNombre: string) => {
const items = cargarItems();
const item = items.find(item => item.id === id);
if (item) {
item.nombre = nuevoNombre;
guardarItems(items);
}
};
// Renderizando la lista de items
const renderizarItems = () => {
const items = cargarItems();
listaItems.innerHTML = '';
items.forEach(item => {
const listItem = document.createElement('li');
listItem.className = 'list-group-item';
listItem.textContent = item.nombre;
listaItems.appendChild(listItem);
// Añadiendo eventos para editar y eliminar el item
listItem.addEventListener('dblclick', () => {
const nuevoNombre = prompt('Editar item:', item.nombre);
if (nuevoNombre !== null) editarItem(item.id, nuevoNombre);
renderizarItems();
});
});
};
// Inicializando la aplicación
formularioItem.addEventListener('submit', (e) => {
e.preventDefault();
const nombre = inputItem.value.trim();
if (nombre) {
agregarItem(nombre);
inputItem.value = '';
renderizarItems();
}
});
// Renderizando items al cargar la página
renderizarItems();
¡Perfecto! Ya tenemos nuestro app.ts
todo listo, lleno de funcionalidades y con una pizca de aprendizaje sobre TypeScript. Ahora, vamos a entrar en una parte igualmente emocionante: compilar nuestro código TypeScript a JavaScript, incluirlo en nuestro index.html
y ¡finalmente ver nuestra aplicación cobrar vida!
¡Entonces, comencemos! Abre la terminal y, en el directorio del proyecto, ingresa el siguiente comando:
tsc
Este comando instruirá a TypeScript a compilar el archivo app.ts
a JavaScript, generando un archivo app.js
. ¿Recuerdas nuestro tsconfig.json
? Ya está configurado para ayudar en este proceso, así que no necesitamos preocuparnos por detalles adicionales por el momento.
¿Recuerdas que, cuando preparábamos nuestro ambiente, descargamos TypeScript globalmente? Es por eso que ahora podemos ejecutar el comando
tsc
, porque NPM descargó en nuestra computadora todo lo que TypeScript necesita. Es bueno ver cómo las cosas se conectan, ¿no?
Con el app.js
generado, necesitamos incluirlo en nuestro index.html
. Para eso, agrega la siguiente etiqueta <script>
al final del <body>
de tu HTML:
<script src="app.js"></script>
De esta forma, estamos conectando el archivo JavaScript generado a nuestro HTML, y todo el código que escribimos en TypeScript será ejecutado correctamente en nuestro navegador.
¡Ahora es el momento que estábamos esperando! Abre el archivo index.html
en tu navegador preferido y ve tu aplicación en acción. Deberías ser capaz de agregar y remover ítems de la lista de compras, y, gracias al LocalStorage, estos ítems se mantendrán incluso si haces un refresh en la página.
Aprovecha para probar la edición; en mi caso, agregué un listener para un doble clic en el ítem.
He dejado mi proyecto final para ti aquí en GitHub.
Bueno, ¡llegamos al final de esta jornada inicial por TypeScript! Es natural que, cuando ya tenemos un cierto conocimiento en JavaScript, podamos sentir cierta desconfianza en relación a TypeScript. Es como salir de la zona de confort, ¿no es así? Pero créeme, ¡es un paso que realmente vale la pena!
En proyectos más grandes, y especialmente en aquellos que no escribimos desde cero, TypeScript se convierte en un aliado invaluable. Nos ayuda a comprender cómo funcionan las cosas, previene una serie de errores comunes y facilita el mantenimiento del código.
Si te gustó esta introducción y quieres continuar profundizando en TypeScript, ¡tengo un gran consejo! Echa un vistazo a la formación sobre TypeScript aquí en Alura Latam. ¡En ella aprenderás aún más con el instructor Leonardo! Exploraremos muchos otros recursos y desafíos que este lenguaje tiene para ofrecer.
Y, claro, ¡recuerda seguirme en las redes sociales! Siempre estoy compartiendo consejos, insights y contenido valioso sobre frontend y otras tecnologías. ¡Es una excelente forma de continuar esta conversación y profundizar aún más en el conocimiento!
Estamos listos, espero que esta aventura haya sido de tu agrado. ¡Estoy emocionado por ver lo que vas a crear con TypeScript! Recuerda, el aprendizaje es continuo y cada nuevo desafío es una oportunidad de crecimiento.
Comparte tu lista de compras con el hashtag #aprendienalura y ¡etiquétame!
¡Larga vida y prosperidad!
Este artículo fue traducido y adaptado por Ingrid Silva
Vinicios Neves Vinicios es ingeniero de software, involucrado en la arquitectura, diseño e implementación de microservicios, micro frontends y sistemas distribuidos. Tiene experiencia significativa en aplicaciones, integración y arquitectura corporativa. Es Ingeniero de Software por la UNESA y Arquitecto de Software por la PUC Minas.
Cursos de Programación, Front End, Data Science, Innovación y Gestión.
Luri es nuestra inteligencia artificial que resuelve dudas, da ejemplos prácticos y ayuda a profundizar aún más durante las clases. Puedes conversar con Luri hasta 100 mensajes por semana
Paga en moneda local en los siguientes países
Cursos de Programación, Front End, Data Science, Innovación y Gestión.
Luri es nuestra inteligencia artificial que resuelve dudas, da ejemplos prácticos y ayuda a profundizar aún más durante las clases. Puedes conversar con Luri hasta 100 mensajes por semana
Paga en moneda local en los siguientes países
Puedes realizar el pago de tus planes en moneda local en los siguientes países:
País | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
Plan Semestral |
486.67
BOB |
69704.84
CLP |
290438.59
COP |
65.90
USD |
259.47
PEN |
1441.54
MXN |
3031.66
UYU |
65.90
USD |
533.19
GTQ |
34952.61
CRC |
4234.92
DOP |
Plan Anual |
737.76
BOB |
105667.89
CLP |
440285.52
COP |
99.90
USD |
393.34
PEN |
2185.29
MXN |
4595.79
UYU |
99.90
USD |
808.29
GTQ |
52985.83
CRC |
6419.86
DOP |
Acceso a todos
los cursos
Estudia las 24 horas,
dónde y cuándo quieras
Nuevos cursos
cada semana