TypeScript es un superconjunto de JavaScript que sigue la especificación ECMAScript 6 (ES6) y es compatible con ECMAScript 5 (ES5).
Para experimentar con TypeScript, puedes usar el compilador en línea en TypeScript Playground.
Para instalar el compilador localmente, usa npm:
npm install -g typescript
tsc -version
Para compilar un archivo TypeScript (.ts) a JavaScript (.js), utiliza el comando:
tsc nombre_archivo.ts
Cadenas de Texto
Cadenas Multilínea
Las cadenas multilínea se definen utilizando acentos graves (``) y permiten saltos de línea directos dentro de ellas.
let mensajeMultilinea = `<div>
<span>Hola Mundo</span>
</div>`;
Plantillas de Cadenas
Dentro de las cadenas multilínea, puedes incrustar expresiones usando la sintaxis ${expresion}. Esta característica está disponible únicamente en cadenas multilínea.
let nombre = "Juan Pérez";
let edad = 26;
let htmlPlantilla = `
<div>
<span>Mi nombre es ${nombre}</span>
<span>Mi edad es ${edad}</span>
</div>
`;
Descomposición Automática de Cadenas
Cuando una cadena multilínea se utiliza para llamar a una función, las expresiones incrustadas se pasan como argumentos a dicha función. La cadena se descompone en un array de partes literales.
function procesarPlantilla(partes: TemplateStringsArray, ...valores: any[]) {
console.log("Partes:", partes);
console.log("Valores:", valores);
}
let nombreUsuario = "Ana Gómez";
let edadUsuario = 22;
procesarPlantilla`Mi nombre es ${nombreUsuario}, mi edad es ${edadUsuario} y tengo más datos`;
// Salida esperada para 'partes': ["Mi nombre es ", ", mi edad es ", " y tengo más datos"]
// Salida esperada para 'valores': ["Ana Gómez", 22]
Variables
Especificación de Tipos de Variables
Al declarar variables, puedes especificar su tipo (por ejemplo, number, string, boolean, Array). Usa any si no deseas restringir el tipo.
TypeScript detectraá errores si la asignación no coincide con el tipo declarado. Sin embargo, estos errores son de compilación de TypeScript; el JavaScript resultante se ejecutará sin problemas.
let nombre: string = "Carlos";
let esMayorDeEdad: boolean = true;
let contador: number = 123;
let datoDinamico: any = 666;
let listaNumeros: Array<number> = [1, 2, 3];
// Sintaxis alternativa para arrays:
let otraListaNumeros: number[] = [1, 2, 3];</number>
Tipos en Funciones
Puedes especificar los tipos de los parámetros y el valor de retorno de una función.
function saludar(nombre: string): string {
return `Hola, ${nombre}`;
}
Para funciones que no retornan ningún valor, usa void.
function mostrarMensaje(mensaje: string): void {
console.log(mensaje);
}
Tipos Personalizados
Puedes usar clases como tipos para tus variables.
class Persona {
nombre: string;
edad: number;
}
let personaEjemplo: Persona = new Persona();
Inferencia de Tipos
Si no se especifica un tipo y no se usa any, TypeScript inferirá el tipo de la variable basándose en el valor inicial asignado.
let ciudad = "Madrid"; // TypeScript infiere que 'ciudad' es de tipo string.
Valores Predeterminados para Variables
Los valores predeterminados se pueden asignar directamente a las variables.
let nombrePredeterminado: string = "Invitado";
Valores Predeterminados en Parámetros de Funciones
En TypeScript, si una función tiene parámetros definidos y se llama con menos argumentos, generará un error (a diferencia de JavaScript, donde los parámetros faltantes son undefined). Los valores predeterminados permiten omitir argumentos opcionales.
Generalmente, los parámetros con valores predeterminados se colocan al final de la lista de parámetros.
function configurarOpciones(opcion1: string, opcion2: string, opcion3: string = 'valorDefecto') {
console.log(opcion1);
console.log(opcion2);
console.log(opcion3);
}
configurarOpciones("a", "b", "c"); // Salida: a, b, c
configurarOpciones("a", "b"); // Salida: a, b, valorDefecto
Parámetros Opcionales en Funciones
Se marca un parámetro como opcional añadiendo un signo de interrogación (?) después de su nombre.
function procesarDatos(datoObligatorio: string, datoOpcional?: number) {
console.log(datoObligatorio);
console.log(datoOpcional);
}
Los parámetros opcionales y los parámetros con valores predeterminados deben ir después de los parámetros obligatorios.
Nuevas Características de Funciones
Rest y Spread Operators (...)
El operador de tres puntos (...) se utiliza para manejar un número variable de argumentos.
1. En la declaración de funciones
Como parámetro, representa un array con un número indeterminado de argumentos.
function sumarTodos(...numeros: number[]): number {
return numeros.reduce((total, num) => total + num, 0);
}
console.log(sumarTodos(1, 2, 3)); // Salida: 6
console.log(sumarTodos(8, 9, 10, 11, 12)); // Salida: 50
2. En la invocación de funciones
Al llamar a una función, se utiliza para expandir los elementos de un array como argumentos individuales.
function multiplicar(a: number, b: number, c: number) {
console.log(a * b * c);
}
let factores1 = [2, 3];
let factores2 = [5, 6, 7];
multiplicar(...factores1, 4); // Equivalente a multiplicar(2, 3, 4) -> Salida: 24
multiplicar(...factores2); // Equivalente a multiplicar(5, 6, 7) -> Salida: 210
Nota: El uso de Spread en la invocación de funciones es una característica de ES6. TypeScript permite su uso y lo compila correctamente.
Funciones Generadoras (Generators)
Las funciones generadoras permiten controlar la ejecución de una función, pausándola y reanudándola manualmente, similar a un punto de interrupción.
- Se declaran con un asterisco después de
function:function* miGenerador() {}. - Se usa la palabra clave
yieldpara pausar la ejecución y opcionalmente retornar un valor. - La llamada a una función generadora no ejecuta su código de inmediato, sino que devuelve un objeto iterador.
- Se llama al método
next()del iterador para ejecutar la función hasta el sigueinteyield. - El valor retornado por
yieldse accede a través de la propiedadvaluedel objeto devuelto pornext().
function* secuenciaNumeros() {
console.log("Inicio de la secuencia");
yield 10; // Pausa 1
console.log("Continuando...");
yield 20; // Pausa 2
console.log("Fin de la secuencia");
}
const iterador = secuenciaNumeros();
console.log(iterador.next()); // Ejecuta hasta el primer yield, imprime "Inicio de la secuencia", retorna { value: 10, done: false }
console.log(iterador.next()); // Ejecuta hasta el segundo yield, imprime "Continuando...", retorna { value: 20, done: false }
console.log(iterador.next()); // Ejecuta el resto, imprime "Fin de la secuencia", retorna { value: undefined, done: true }
Nota: Las funciones generadoras son una característica de ES6. TypeScript las soporta para la compilación.
Expresiones de Flecha y Bucles
Funciones de Flecha (Arrow Functions)
Las funciones de flecha usan la sintaxis => en lugar de la palabra clave function.
// Función tradicional
function saludarTradicional() {
console.log("Hola");
}
// Función de flecha
const saludarFlecha = () => {
console.log("Hola");
};
Usos Comunes
Son especialmente útiles para funciones anónimas, como callbacks en setTimeout, filter, setInterval, etc.
setTimeout((a, b) => {
console.log(a + b);
}, 1000);
Sintaxis Abreviada
Para funciones de una sola línea, se puede omitir la palabra clave return y las llaves.
// Equivalente a:
// setTimeout(function(a, b){ return a + b; }, 1000);
setTimeout((a, b) => a + b, 1000);
// Si hay un solo parámetro, se pueden omitir los paréntesis:
setTimeout(arg => console.log(arg), 1000);
El this en Funciones de Flecha
Las funciones de flecha resuelven el problema del this en JavaScript. A diferencia de las funciones tradicionales, donde this puede cambiar de contexto (a menudo apuntando a window en callbacks asíncronos), las funciones de flecha heredan el this de su entorno léxico (el this del scope donde fueron definidas).
class PersonaTradicional {
constructor() {
this.nombre = "Carlos";
setTimeout(function() {
// 'this' aquí se refiere a window (o undefined en modo estricto)
console.log(this.nombre); // undefined
}, 1000);
}
}
class PersonaFlecha {
constructor() {
this.nombre = "Ana";
setTimeout(() => {
// 'this' aquí se refiere a la instancia de PersonaFlecha
console.log(this.nombre); // "Ana"
}, 1000);
}
}
// new PersonaTradicional();
// new PersonaFlecha();
Bucle for...of
El bucle for...of (característica de ES6 soportada por TypeScript) permite iterar sobre elementos de estructuras iterables como arrays, strings y objetos Map/Set. A diferencia de forEach, soporta break y continue.
const numeros = [1, 2, 3, 4, 5, 6];
for (const numero of numeros) {
if (numero >= 2 && numero <= 3) {
continue; // Salta a la siguiente iteración
}
console.log(numero); // Salida: 1, 4, 5, 6
}
Características de Programación Orientada a Objetos
Clases
Declaración
Las clases se declaran con la palabra clave class. La instanciación se realiza con new. Las propiedades y métodos se declaran directamente dentro de la clase.
Al compilar, los métodos de clase se colocan en el prototipo, mientras que las propiedades se inicializan dentro del constructor.
class Vehiculo {
velocidadActual: number;
readonly marca: string; // Propiedad de solo lectura
constructor(marcaVehiculo: string) {
this.marca = marcaVehiculo;
this.velocidadActual = 0;
console.log(`Vehículo ${this.marca} creado.`);
}
acelerar(velocidad: number): void {
this.velocidadActual += velocidad;
console.log(`Acelerando. Velocidad actual: ${this.velocidadActual} km/h`);
}
}
const miCoche = new Vehiculo("Toyota");
miCoche.acelerar(50);
Modificadores de Acceso
TypeScript soporta public, private y protected.
public(por defecto): Accesible desde cualquier lugar.private: Accesible solo dentro de la clase donde se declara.protected: Accesible dentro de la clase y por sus clases derivadas (subclases).
Constructor
El código dentro del constructor se ejecuta una sola vez al instanciar la clase. Es un punto de entrada para pasar parámetros al crear un objeto.
Los parámetros del constructor no se convierten automáticamente en propiedades de la clase, a menos que se les aplique un modificador de acceso.
class Producto {
// Propiedad declarada explícitamente
nombre: string;
// Parámetro del constructor con modificador de acceso
// Crea automáticamente la propiedad 'id' y la inicializa
constructor(nombreProducto: string, public id: number) {
this.nombre = nombreProducto;
console.log(`Producto ${this.nombre} con ID ${this.id} creado.`);
}
}
const libro = new Producto("El Quijote", 101);
// console.log(libro.id); // Acceso a la propiedad generada por el modificador
// console.log(libro.nombre); // Acceso a la propiedad declarada explícitamente
Herencia de Clases
Se utiliza la palabra clave extends para establecer una relación de herencia.
class Animal {
nombre: string;
constructor(nombreAnimal: string) {
this.nombre = nombreAnimal;
}
hacerSonido(): void {
console.log("Sonido genérico");
}
}
class Perro extends Animal {
constructor(nombrePerro: string) {
super(nombrePerro); // Llama al constructor de la clase padre
}
hacerSonido(): void {
console.log("Guau!"); // Sobrescribe el método del padre
}
ladrar(): void {
console.log("Ladrando fuerte...");
}
}
const miPerro = new Perro("Fido");
miPerro.hacerSonido(); // Salida: Guau!
miPerro.ladrar(); // Salida: Ladrando fuerte...
Si una subclase tiene su propio constructor, debe llamar a super() para ejecutar el constructor de la superclase antes de acceder a this.
Genéricos (Generics)
Los genéricos permiten crear componentes (clases, funciones, tipos) que funcionan con una variedad de tipos, manteniendo la seguridad de tipos.
class Contenedor<T> {
private valor: T;
constructor(valorInicial: T) {
this.valor = valorInicial;
}
obtenerValor(): T {
return this.valor;
}
}
// Usando el genérico con string
let contenedorString = new Contenedor<string>("Hola Mundo Genérico");
console.log(contenedorString.obtenerValor().toUpperCase()); // Acceso seguro a métodos de string
// Usando el genérico con number
let contenedorNumero = new Contenedor<number>(12345);
console.log(contenedorNumero.obtenerValor().toFixed(2)); // Acceso seguro a métodos de number
Interfaces
Las interfaces definen contratos de estructura para objetos o clases. Obligan a que las implementaciones cumplan con las propiedades y métodos especificados.
1. Definición de una interfaz
interface ConfiguracionUsuario {
nombre: string;
edad?: number; // Propiedad opcional
readonly id: number; // Propiedad de solo lectura
}
2. Uso de interfaces como parámetros de función/constructor
function crearUsuario(config: ConfiguracionUsuario): void {
console.log(`Usuario creado: ${config.nombre}, ID: ${config.id}`);
if (config.edad) {
console.log(`Edad: ${config.edad}`);
}
}
const miConfiguracion: ConfiguracionUsuario = {
id: 1,
nombre: "Elena"
// edad es opcional
};
crearUsuario(miConfiguracion);
3. Uso de interfaces en clases
interface Volador {
volar(): void;
}
class Avion implements Volador {
volar(): void {
console.log("El avión está despegando.");
}
}
class Pato implements Volador {
volar(): void {
console.log("El pato está volando bajo.");
}
}
Módulos
Un archivo de TypeScript se considera un módulo. Los módulos permiten encapsular código y exportar explícitamente lo que se desea compartir (clases, variables, funciones).
export se usa para exponer miembros de un módulo.
import se usa para consumir miembros de otros módulos.
// archivo: utilidades.ts
export const PI = 3.14159;
export function calcularCircunferencia(radio: number): number {
return 2 * PI * radio;
}
// archivo: principal.ts
import { calcularCircunferencia, PI } from './utilidades';
console.log(`El valor de PI es: ${PI}`);
console.log(`Circunferencia de radio 5: ${calcularCircunferencia(5)}`);
Anotaciones (Decorators)
Los decoradores son una característica experimental (similar a las anotaciones en otros lenguajes) que permiten añadir metadatos a clases, métodos, propiedades o parámetros. Son comúnmente utilizados por frameworks como Angular.
Para usarlos, a menudo se necesita habilitar la opción experimentalDecorators en el archivo tsconfig.json.
// Ejemplo conceptual de un decorador de clase
function logearClase(constructor: Function) {
console.log(`Clase ${constructor.name} ha sido definida.`);
}
@logearClase
class EjemploDecorador {
constructor() {
console.log("Instancia de EjemploDecorador creada.");
}
}
const instancia = new EjemploDecorador(); // Imprimirá "Clase EjemploDecorador ha sido definida." y "Instancia de EjemploDecorador creada."
Frameworks como Angular utilizan decoradores como @Component, @Injectable, @Input, @Output para configurar y estructurar aplicaciones.
Archivos de Definición de Tipos (.d.ts)
Para usar bibliotecas de JavaScript de terceros (como jQuery) en TypeScript y beneficiarse de la verificación de tipos, necesitas sus archivos de definición de tipos. Estos archivos describen la API de la biblioteca en términos de tipos de TypeScript.
Herramientas como npm o typings se utilizan para instalar estos archivos de definición (por ejemplo, npm install --save-dev @types/jquery).
// Ejemplo: Uso de jQuery después de instalar sus tipos (@types/jquery)
import * as $ from 'jquery';
$(document).ready(() => {
$('button').on('click', () => {
alert('Botón clickeado!');
});
});