TypeScript es uno de esos lenguajes que, una vez que lo pruebas, no quieres volver a escribir JavaScript puro. Es el lenguaje que usa Angular de forma obligatoria, el que cada vez más proyectos React adoptan y el que las empresas más demandan en 2026. En este artículo te explico qué es, por qué deberías aprenderlo y cómo funciona desde cero.

En este artículo:

¿Qué es TypeScript?

TypeScript es un lenguaje de programación desarrollado por Microsoft que añade tipado estático opcional a JavaScript. Dicho de forma sencilla: es JavaScript con tipos.

TypeScript no se ejecuta directamente en el navegador — se compila a JavaScript puro. Esto significa que cualquier código JavaScript válido es también código TypeScript válido. Puedes migrar un proyecto JavaScript a TypeScript de forma gradual, archivo por archivo.

Fue lanzado por Microsoft en 2012 y su adopción ha crecido de forma constante hasta convertirse en uno de los lenguajes más populares del mundo. Según la encuesta de Stack Overflow 2024, TypeScript es el quinto lenguaje más usado y uno de los más valorados por los desarrolladores.

TypeScript vs JavaScript: diferencias clave

La diferencia fundamental es el tipado estático. En JavaScript, los errores de tipo se descubren en tiempo de ejecución — cuando el código ya está corriendo. En TypeScript, se descubren en tiempo de compilación — antes de ejecutar nada.

Este ejemplo ilustra la diferencia:

// JavaScript — sin errores hasta que se ejecuta
function sumar(a, b) {
  return a + b;
}

sumar(5, "10"); // Devuelve "510" en vez de 15 — bug silencioso

// TypeScript — error detectado antes de ejecutar
function sumar(a: number, b: number): number {
  return a + b;
}

sumar(5, "10"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'

Otras ventajas importantes de TypeScript sobre JavaScript:

Tipos básicos

TypeScript incluye todos los tipos primitivos de JavaScript y añade algunos propios:

// Tipos primitivos
let nombre: string = "Ada";
let edad: number = 28;
let activo: boolean = true;

// Arrays
let numeros: number[] = [1, 2, 3];
let nombres: Array<string> = ["Ana", "Luis", "María"];

// Tuplas — array con tipos fijos en posiciones concretas
let coordenadas: [number, number] = [40.416, -3.703];
let entrada: [string, number] = ["Ada", 28];

// Any — desactiva el tipado (úsalo lo menos posible)
let cualquierCosa: any = "hola";
cualquierCosa = 42; // válido pero pierde las ventajas del tipado

// Unknown — más seguro que any
let valorDesconocido: unknown = obtenerValor();
if (typeof valorDesconocido === "string") {
  console.log(valorDesconocido.toUpperCase()); // seguro
}

// Void — función que no devuelve nada
function saludar(nombre: string): void {
  console.log(`Hola, ${nombre}`);
}

// Never — función que nunca termina o siempre lanza error
function lanzarError(mensaje: string): never {
  throw new Error(mensaje);
}

// Union types — puede ser uno u otro tipo
let id: number | string = 123;
id = "abc-456"; // también válido

// Literal types — solo puede ser ese valor concreto
let direccion: "norte" | "sur" | "este" | "oeste" = "norte";

// Optional — puede ser undefined
let telefono: string | undefined;
let email?: string; // equivalente a string | undefined

Interfaces y tipos

Las interfaces son la forma principal de definir la forma de un objeto en TypeScript. Son una de las funcionalidades más usadas en proyectos reales:

// Interface básica
interface Usuario {
  id: number;
  nombre: string;
  email: string;
  edad?: number;        // propiedad opcional
  readonly creado: Date; // solo lectura — no se puede modificar después de crear
}

// Uso de la interface
const usuario: Usuario = {
  id: 1,
  nombre: "Ada",
  email: "ada@ejemplo.com",
  creado: new Date()
};

// Interface con métodos
interface Repositorio<T> {
  getById(id: number): Promise<T>;
  getAll(): Promise<T[]>;
  save(entidad: T): Promise<T>;
  delete(id: number): Promise<void>;
}

// Extender interfaces
interface Empleado extends Usuario {
  empresa: string;
  salario: number;
}

// Type alias — similar a interface pero más flexible
type Coordenadas = {
  lat: number;
  lng: number;
};

type Resultado<T> = {
  data: T | null;
  error: string | null;
  cargando: boolean;
};

// Intersection types — combina tipos
type UsuarioConRol = Usuario & { rol: "admin" | "user" | "moderador" };

// Diferencia entre interface y type:
// - interface: mejor para definir objetos, se puede extender y combinar
// - type: mejor para uniones, intersecciones y tipos complejos

Clases y orientación a objetos

TypeScript potencia enormemente las clases de JavaScript con modificadores de acceso, propiedades readonly y otras características del paradigma orientado a objetos — muy familiar si vienes de Java:

class Persona {
  // Modificadores de acceso
  public nombre: string;        // accesible desde cualquier lugar
  private _edad: number;        // solo accesible desde la clase
  protected email: string;      // accesible desde la clase y subclases
  readonly id: number;          // no se puede modificar

  // Constructor shorthand — define y asigna en una línea
  constructor(
    public nombre: string,
    private _edad: number,
    protected email: string,
    readonly id: number
  ) {}

  // Getter
  get edad(): number {
    return this._edad;
  }

  // Setter con validación
  set edad(valor: number) {
    if (valor < 0 || valor > 150) {
      throw new Error("Edad no válida");
    }
    this._edad = valor;
  }

  // Método público
  saludar(): string {
    return `Hola, soy ${this.nombre}`;
  }
}

// Herencia
class Desarrollador extends Persona {
  constructor(
    nombre: string,
    edad: number,
    email: string,
    id: number,
    public lenguajes: string[]
  ) {
    super(nombre, edad, email, id);
  }

  presentarse(): string {
    return `${this.saludar()} y sé ${this.lenguajes.join(", ")}`;
  }
}

// Implementar interfaces
interface Serializable {
  toJSON(): string;
}

class Producto implements Serializable {
  constructor(
    public nombre: string,
    public precio: number
  ) {}

  toJSON(): string {
    return JSON.stringify({ nombre: this.nombre, precio: this.precio });
  }
}

Generics

Los generics permiten escribir código reutilizable que funciona con cualquier tipo. Son uno de los conceptos más potentes de TypeScript y los verás constantemente en Angular y otras librerías:

// Función genérica básica
function primerElemento<T>(array: T[]): T | undefined {
  return array[0];
}

const numero = primerElemento([1, 2, 3]);     // TypeScript sabe que es number
const nombre = primerElemento(["Ana", "Luis"]); // TypeScript sabe que es string

// Clase genérica
class Pila<T> {
  private elementos: T[] = [];

  push(elemento: T): void {
    this.elementos.push(elemento);
  }

  pop(): T | undefined {
    return this.elementos.pop();
  }

  get tamaño(): number {
    return this.elementos.length;
  }
}

const pilaNumeros = new Pila<number>();
pilaNumeros.push(1);
pilaNumeros.push(2);

// Constraints — limitar qué tipos acepta el genérico
function obtenerPropiedad<T, K extends keyof T>(objeto: T, clave: K): T[K] {
  return objeto[clave];
}

const usuario = { nombre: "Ada", edad: 28 };
const nombre2 = obtenerPropiedad(usuario, "nombre"); // string
const edad = obtenerPropiedad(usuario, "edad");       // number
// obtenerPropiedad(usuario, "email"); // Error: no existe esa propiedad

// Utility types — tipos genéricos incluidos en TypeScript
interface Producto {
  id: number;
  nombre: string;
  precio: number;
  descripcion: string;
}

type ProductoParcial = Partial<Producto>;    // todas las propiedades opcionales
type ProductoRequerido = Required<Producto>; // todas las propiedades requeridas
type ProductoSoloLectura = Readonly<Producto>; // todas las propiedades readonly
type ProductoResumen = Pick<Producto, "id" | "nombre">; // solo id y nombre
type ProductoSinId = Omit<Producto, "id">;  // todo menos id

Decoradores

Los decoradores son una funcionalidad experimental de TypeScript que añade metadatos a clases, métodos y propiedades. Son fundamentales en Angular — prácticamente todo en Angular usa decoradores:

// En Angular, los decoradores son ubicuos
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {

  @Input() titulo: string = '';      // recibe datos del componente padre
  @Output() click = new EventEmitter(); // emite eventos al componente padre

  @ViewChild('miElemento') elemento!: ElementRef; // referencia a elemento del DOM
}

@Injectable({ providedIn: 'root' })   // servicio inyectable
export class UsuarioService {

  constructor(private http: HttpClient) {}

  @HostListener('click')             // escucha eventos del DOM
  onClick() { /* ... */ }
}

TypeScript en Angular

Si estás aprendiendo Angular, TypeScript no es opcional — es obligatorio. Pero lejos de ser un obstáculo, TypeScript hace Angular mucho más cómodo de usar. Aquí un ejemplo de cómo se usan los tipos en un componente Angular real:

// Interfaz del modelo
interface Producto {
  id: number;
  nombre: string;
  precio: number;
  categoria: 'electronica' | 'ropa' | 'alimentacion';
}

// Interfaz del estado del componente
interface EstadoProductos {
  productos: Producto[];
  cargando: boolean;
  error: string | null;
  filtro: string;
}

@Component({
  selector: 'app-productos',
  standalone: true,
  imports: [CommonModule, FormsModule],
  templateUrl: './productos.component.html'
})
export class ProductosComponent implements OnInit {

  estado: EstadoProductos = {
    productos: [],
    cargando: false,
    error: null,
    filtro: ''
  };

  constructor(private productoService: ProductoService) {}

  ngOnInit(): void {
    this.cargarProductos();
  }

  cargarProductos(): void {
    this.estado.cargando = true;
    this.productoService.getAll().subscribe({
      next: (productos: Producto[]) => {
        this.estado.productos = productos;
        this.estado.cargando = false;
      },
      error: (err: Error) => {
        this.estado.error = err.message;
        this.estado.cargando = false;
      }
    });
  }

  get productosFiltrados(): Producto[] {
    return this.estado.productos.filter(p =>
      p.nombre.toLowerCase().includes(this.estado.filtro.toLowerCase())
    );
  }
}

Si además conectas Angular con Spring Boot en el backend, TypeScript te permite definir las mismas interfaces que el backend Java — el código es más predecible y los errores de integración prácticamente desaparecen.

Configuración: tsconfig.json

El archivo tsconfig.json controla cómo TypeScript compila tu código. En Angular ya viene configurado, pero es útil entender las opciones más importantes:

{
  "compilerOptions": {
    "target": "ES2022",           // versión de JS a la que compila
    "module": "ESNext",           // sistema de módulos
    "strict": true,               // activa todas las comprobaciones estrictas
    "noImplicitAny": true,        // error si TypeScript infiere 'any'
    "strictNullChecks": true,     // null y undefined son tipos separados
    "noUnusedLocals": true,       // error si hay variables no usadas
    "noUnusedParameters": true,   // error si hay parámetros no usados
    "esModuleInterop": true,      // compatibilidad con módulos CommonJS
    "skipLibCheck": true,         // no comprueba tipos en node_modules
    "outDir": "./dist",           // directorio de salida
    "rootDir": "./src"            // directorio de entrada
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

El flag strict: true es el más importante — activa todas las comprobaciones estrictas de TypeScript. Es lo que más errores te va a atrapar antes de que lleguen a producción.

Errores más comunes

❌ Usar any en vez de unknown

Cuando no sabes el tipo de un valor, usa unknown en lugar de any. any desactiva todas las comprobaciones de TypeScript en ese valor. unknown te obliga a comprobar el tipo antes de usarlo, que es exactamente lo que quieres.

❌ Afirmaciones de tipo innecesarias

// ❌ Mal — forzar el tipo sin comprobarlo
const elemento = document.getElementById("mi-id") as HTMLInputElement;

// ✅ Bien — comprobar que existe antes
const elemento = document.getElementById("mi-id");
if (elemento instanceof HTMLInputElement) {
  console.log(elemento.value);
}

❌ Interfaces demasiado genéricas

Definir interfaces con todas las propiedades como opcionales (?) elimina las ventajas del tipado. Sé específico — marca como opcional solo lo que realmente puede no existir.

❌ No aprovechar los Utility Types

TypeScript incluye Partial, Required, Pick, Omit, Record y otros tipos de utilidad que evitan duplicar código. Úsalos en vez de crear interfaces similares desde cero.

¿Por qué aprender TypeScript en 2026?

TypeScript ya no es opcional en el desarrollo web profesional — es el estándar. Angular lo usa de forma obligatoria. React y Vue lo adoptan en todos los proyectos serios. Node.js tiene soporte nativo experimental. Las empresas lo exigen en prácticamente todas las ofertas de trabajo frontend y full stack.

Si estás aprendiendo Angular o quieres trabajar en empresas medianas o grandes, TypeScript es el siguiente paso natural después de JavaScript. La curva de aprendizaje es suave si ya sabes JavaScript — en una semana puedes estar escribiendo TypeScript productivamente.

El mejor consejo es empezar con strict: false en un proyecto existente y ir añadiendo tipos gradualmente. No intentes tiparlo todo desde el principio — añade tipos donde más valor aporten y ve aumentando la cobertura con el tiempo.