Manual Completo de TypeScript

Introducción a TypeScript

TypeScript es un superset de JavaScript que añade tipos estáticos y otras características avanzadas. Se compila a JavaScript puro.

Beneficios de TypeScript:

Configuración inicial

# Instalar TypeScript globalmente
npm install -g typescript

# Inicializar un proyecto
tsc --init  # Crea tsconfig.json

# Compilar archivos TypeScript
tsc archivo.ts

# Modo observador (compilación automática)
tsc --watch

tsconfig.json básico

{
  "compilerOptions": {
    "target": "ES6",          // Versión de JS a generar
    "module": "commonjs",     // Sistema de módulos
    "strict": true,          // Habilita todas las verificaciones de tipo
    "outDir": "./dist",      // Carpeta de salida
    "rootDir": "./src",      // Carpeta de origen
    "esModuleInterop": true  // Mejor interoperabilidad
  },
  "include": ["src/**/*"]    // Archivos a incluir
}

Tipos Básicos

Tipos primitivos

let nombre: string = "Juan";
let edad: number = 30;
let esActivo: boolean = true;

// Arrays
let numeros: number[] = [1, 2, 3];
let frutas: Array<string> = ["manzana", "pera"];

// Tuplas (arreglos con tipos fijos)
let persona: [string, number] = ["Juan", 30];

Tipos especiales

// Any (evitar cuando sea posible)
let variableDinamica: any = "puede ser cualquier cosa";
variableDinamica = 42;

// Void (para funciones que no retornan valor)
function mostrarMensaje(): void {
    console.log("Hola");
}

// Never (para funciones que nunca terminan)
function lanzarError(mensaje: string): never {
    throw new Error(mensaje);
}

Uniones y alias de tipos

// Unión de tipos
let id: string | number = "ABC123";
id = 123;

// Alias de tipos
type ID = string | number;
let userId: ID = "USER123";

// Literal types
type Direccion = "norte" | "sur" | "este" | "oeste";
let direccion: Direccion = "norte";

Interfaces y Tipos de Objeto

Interfaces básicas

interface Persona {
    nombre: string;
    edad: number;
    direccion?: string; // Propiedad opcional
}

function saludar(persona: Persona): void {
    console.log(`Hola ${persona.nombre}`);
}

const juan: Persona = {
    nombre: "Juan",
    edad: 30
};

Extender interfaces

interface Empleado extends Persona {
    puesto: string;
    salario: number;
}

const empleado: Empleado = {
    nombre: "María",
    edad: 28,
    puesto: "Desarrolladora",
    salario: 50000
};

Tipos vs Interfaces

Diferencias clave:

Funciones en TypeScript

Tipado de funciones

// Función normal
function sumar(a: number, b: number): number {
    return a + b;
}

// Función flecha
const multiplicar: (a: number, b: number) => number = 
    (a, b) => a * b;

// Parámetros opcionales
function crearUsuario(
    nombre: string,
    edad?: number  // El '?' indica opcional
): void {
    // ...
}

Sobrecarga de funciones

// Declaraciones de sobrecarga
function obtener(id: number): Usuario;
function obtener(email: string): Usuario;

// Implementación
function obtener(parametro: number | string): Usuario {
    if (typeof parametro === "number") {
        // Buscar por id
    } else {
        // Buscar por email
    }
}

Clases en TypeScript

Clases básicas

class Persona {
    // Propiedades
    nombre: string;
    private edad: number;
    protected id: string;

    // Constructor
    constructor(nombre: string, edad: number) {
        this.nombre = nombre;
        this.edad = edad;
        this.id = Math.random().toString(36).substr(2, 9);
    }

    // Métodos
    public saludar(): void {
        console.log(`Hola, soy ${this.nombre}`);
    }
}

Modificadores de acceso

Modificadores:

Herencia e implementación

interface SerVivo {
    respirar(): void;
}

class Animal implements SerVivo {
    respirar(): void {
        console.log("Respirando...");
    }
}

class Perro extends Animal {
    constructor(private nombre: string) {
        super();
    }

    ladrar(): void {
        console.log("¡Guau!");
    }
}

Genéricos

Funciones genéricas

function identidad<T>(arg: T): T {
    return arg;
}

let salida1 = identidad<string>("hola");
let salida2 = identidad(42); // Inferencia de tipo

Interfaces genéricas

interface RespuestaAPI<T> {
    data: T;
    status: number;
    mensaje?: string;
}

const respuestaUsuario: RespuestaAPI<Usuario> = {
    data: { id: 1, nombre: "Juan" },
    status: 200
};

Clases genéricas

class Caja<T> {
    private contenido: T;

    constructor(contenido: T) {
        this.contenido = contenido;
    }

    obtenerContenido(): T {
        return this.contenido;
    }
}

const cajaString = new Caja<string>("sorpresa");
const cajaNumero = new Caja(42); // Inferencia

Utilidades Avanzadas

Tipos utilitarios

interface Usuario {
    id: number;
    nombre: string;
    email: string;
    activo: boolean;
}

// Partial: hace todas las propiedades opcionales
type UsuarioParcial = Partial<Usuario>;

// Readonly: hace todas las propiedades readonly
type UsuarioSoloLectura = Readonly<Usuario>;

// Pick: selecciona propiedades específicas
type UsuarioBasico = Pick<Usuario, "id" | "nombre">;

// Omit: omite propiedades específicas
type UsuarioSinID = Omit<Usuario, "id">;

Tipos condicionales

type TipoDeDato<T> = 
    T extends string ? "string" :
    T extends number ? "number" :
    T extends boolean ? "boolean" :
    "otro";

type T1 = TipoDeDato<string>;  // "string"
type T2 = TipoDeDato<Date>;    // "otro"

Tipos mapeados

// Hacer todas las propiedades opcionales y de tipo string
type Stringify<T> = {
    [P in keyof T]?: string;
};

type UsuarioStrings = Stringify<Usuario>;
/*
{
    id?: string;
    nombre?: string;
    email?: string;
    activo?: string;
}
*/

Decoradores (Experimental)

Nota: Los decoradores son una característica experimental en TypeScript. Para usarlos, debes habilitar "experimentalDecorators": true en tsconfig.json.

Decorador de clase

function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

@sealed
class MiClase {
    propiedad = "valor";
}

Decorador de método

function log(
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
) {
    const original = descriptor.value;
    
    descriptor.value = function(...args: any[]) {
        console.log(`Llamando a ${propertyKey} con args:`, args);
        const resultado = original.apply(this, args);
        console.log(`Resultado:`, resultado);
        return resultado;
    };
    
    return descriptor;
}

class Calculadora {
    @log
    sumar(a: number, b: number): number {
        return a + b;
    }
}

Integración con JavaScript

Archivos de declaración (.d.ts)

// tipos.d.ts
declare module "mi-modulo" {
    export function funcion1(param: string): number;
    export const constante1: string;
}

Usar librerías JavaScript

// Instalar tipos para librerías populares
npm install --save-dev @types/lodash @types/express

// Declaración rápida para módulos sin tipos
declare module "modulo-sin-tipos";

Migrar de JavaScript a TypeScript

  1. Renombrar archivos .js a .ts
  2. Configurar tsconfig.json con "allowJs": true
  3. Corregir errores de tipo gradualmente
  4. Agregar tipos a las interfaces principales
  5. Habilitar opciones estrictas progresivamente

Conclusión

TypeScript ofrece un sistema de tipos poderoso que mejora la calidad y mantenibilidad del código JavaScript. Aunque tiene una curva de aprendizaje, los beneficios en proyectos medianos y grandes son significativos.

Recursos adicionales: