Programming Language

TypeScript

TypeScript añade tipos estáticos a JavaScript, detectando errores antes de que lleguen a producción. Aquí tienes qué es, cómo funciona y cómo empezar a usarlo hoy mismo.

beginner20 min readUpdated 15 sept 2026
typescript
function greet(name: string): string {
  return `Hello, ${name}!`;
}

const message = greet("TypeScript");
console.log(message);
Creado
2012, por Microsoft
Tipo
Superset de JavaScript con tipado estático
Compila a
JavaScript puro
Estándar
ECMAScript con extensiones de tipo
Extensión de archivo
.ts / .tsx
Versión actual
TypeScript 5.x

Por que importa

Por qué TypeScript es importante

Detecta errores pronto

Los errores de tipo se detectan en tiempo de compilación, no en producción. Encuentras los fallos antes de que tu código se ejecute.

Mejor experiencia en el editor

El autocompletado, la documentación inline y las herramientas de refactorización funcionan porque el editor entiende la estructura de tu código.

Código autodocumentado

Los tipos describen qué esperan y qué devuelven las funciones. Leer la firma te dice exactamente cómo utilizarla.

La imagen completa

Las tres capas de TypeScript

TypeScript es un lenguaje, un compilador y un sistema de tipos. Entender en qué capa estás trabajando hace que la depuración sea mucho más sencilla.

TypeScript

El lenguaje

JavaScript más anotaciones de tipo, interfaces, genéricos y operaciones de tipo avanzadas.

El compilador

tsc

Convierte TypeScript a JavaScript, verifica los tipos y genera una salida limpia para cualquier target.

El ecosistema

Herramientas y librerías

Soporte de IDE, archivos de declaración, modo estricto y frameworks que ya incluyen TypeScript integrado.

TypeScript de un vistazo

Lo que el lenguaje te ofrece

Tipos primitivos

string, number, boolean, null, undefined, symbol, bigint.

Tipos de objeto

Interfaces, type aliases, clases y arrays.

Uniones e intersecciones

Combina tipos con | (o) y & (y) para modelar datos flexibles.

Type narrowing

Usa typeof, in y discriminantes para que TypeScript refine los tipos dentro de condicionales.

Genéricos

Escribe funciones y tipos reutilizables que funcionen con cualquier tipo manteniendo la seguridad de tipos.

Utility types

Transformaciones integradas como Partial, Pick y Record que modifican tipos existentes.

Una breve historia

De anotaciones opcionales a estándar de la industria

  1. 2012

    Lanzamiento de TypeScript

    Microsoft lanza TypeScript como una capa de tipos opcional para JavaScript.

    12
  2. 2016

    Angular apuesta por TypeScript

    Angular adopta TypeScript como su lenguaje principal, impulsando su adopción en todo el ecosistema.

    16
  3. 2018

    TypeScript 3.0

    Las referencias de proyecto, el tipo unknown y las tuplas hacen que el lenguaje sea más potente.

    18
  4. 2020

    Tuplas variádicas

    TypeScript 4.0 introduce los tipos de tuplas variádicas y elementos de tupla etiquetados.

    20
  5. 2023

    Estandarización de Decorators

    Los decorators de TC39 llegan a TypeScript 5.0, alineándose con la propuesta de ECMAScript.

    23
  6. Hoy

    La elección por defecto

    La mayoría de los proyectos nuevos de JavaScript comienzan con TypeScript. Los frameworks ya lo traen integrado.

    Hoy

La guia completa

TypeScript: Todo lo que necesitas saber

¿Qué es TypeScript?

TypeScript es un superset de JavaScript con tipado estático que añade anotaciones de tipo opcionales que se verifican en tiempo de compilación. Se compila a JavaScript puro, por lo que se ejecuta en cualquier lugar donde JavaScript funcione: navegadores, Node.js, Deno y Bun.

Creado por Microsoft en 2012, TypeScript se ha convertido en la opción predeterminada para proyectos serios de JavaScript. La razón es sencilla: detecta errores antes de que el código se ejecute. Una errata en el nombre de una propiedad, un argumento faltante, un tipo de retorno incorrecto; TypeScript señala estos problemas en tiempo de compilación en lugar de permitir que se conviertan en caídas en producción.

La mejor manera de pensar en TypeScript es como JavaScript con una red de seguridad. Escribes el mismo lenguaje que ya conoces, pero puedes describir la forma de tus datos y los contratos de tus funciones. El compilador verifica que todo encaje y luego elimina los tipos, dejando un JavaScript limpio.

Por qué TypeScript es importante

JavaScript tiene un tipado dinámico, lo que significa que puedes pasar cualquier valor a cualquier función y el error solo aparecerá cuando ese código se ejecute. En un script pequeño esto no es problema, pero en una aplicación grande con decenas de desarrolladores, es una receta para generar bugs en producción.

TypeScript soluciona esto comprobando los tipos antes de que el código se ejecute:

  • Detecta bugs en tiempo de compilación — si escribes mal una propiedad, pasas un tipo de argumento incorrecto o olvidas manejar un valor null, TypeScript te lo indica inmediatamente.
  • Mejor experiencia en el editor — el autocompletado funciona porque el editor sabe qué propiedades tiene un objeto. El refactorizado es seguro porque el compilador encuentra cada lugar que necesita actualizarse.
  • Código autodocumentado — las firmas de las funciones describen exactamente qué esperan y qué devuelven. Leer los tipos te indica cómo usar el código sin necesidad de leer la implementación.
  • Refactorizado más seguro — cambia un tipo y el compilador resaltará cada lugar que se haya roto. Se acabó el buscar errores de ejecución por todo el código base.
  • Adopción progresiva — puedes añadir TypeScript a un proyecto de JavaScript existente de forma incremental, archivo por archivo.

TypeScript no añade sobrecarga (overhead) en tiempo de ejecución. Los tipos se eliminan durante la compilación, por lo que el JavaScript que llega a los usuarios es idéntico, independientemente de si lo escribiste en TypeScript o no.

Cómo funciona TypeScript

El código de TypeScript reside en archivos .ts. El compilador de TypeScript (tsc) lee estos archivos, verifica los tipos y genera archivos .js con las anotaciones de tipo eliminadas.

// app.ts
function add(a: number, b: number): number {
  return a + b;
}

const result = add(1, 2); // result is number

El compilador verifica que a y b sean números, que la función devuelva un número y que result reciba un número. Si algo no coincide, obtendrás un error en tiempo de compilación. El JavaScript resultante es simplemente:

function add(a, b) {
  return a + b;
}
const result = add(1, 2);

Sin tipos, sin sobrecarga. La seguridad estuvo presente únicamente en el paso de compilación.

Anotaciones de tipo

Las anotaciones de tipo describen el tipo de una variable, parámetro o valor de retorno:

// primitives
let name: string = "Ada";
let age: number = 36;
let active: boolean = true;

// arrays
let skills: string[] = ["HTML", "CSS"];
let scores: Array<number> = [95, 87, 92];

// function parameters and return
function greet(name: string): string {
  return `Hello, ${name}!`;
}

No siempre son necesarias las anotaciones. TypeScript infiere los tipos a partir del valor:

let count = 0;          // inferred as number
const items = ["a"];    // inferred as string[]

Prefiere la inferencia cuando el tipo sea obvio. Utiliza anotaciones cuando el tipo no quede claro a partir del valor, cuando necesites un tipo más general o cuando la inferencia predeterminada sea demasiado restrictiva.

Inferencia de tipos

TypeScript deduce automáticamente los tipos a partir del contexto:

// from assignment
let x = 5; // number

// from return value
function doubling(n: number) {
  return n * 2; // inferred as number
}

// from context
const numbers = [1, 2, 3]; // number[]
const doubled = numbers.map(n => n * 2); // number[]

La inferencia significa que escribes menos código manteniendo la misma seguridad. La mayor parte del código en TypeScript depende en gran medida de la inferencia; anota los tipos únicamente cuando aporte claridad.

Union types

Un union type indica que un valor puede ser de uno de varios tipos:

type Status = "loading" | "success" | "error";

function handleStatus(status: Status) {
  if (status === "loading") {
    // TypeScript knows status is "loading" here
  }
}

let value: string | number;
value = "hello";
value = 42;

Los unions modelan muy bien los datos del mundo real. Las respuestas de una API pueden tener éxito o fallar. Un campo puede ser un string o null. Una función puede aceptar diferentes tipos de entrada.

Type narrowing

TypeScript realiza el estrechamiento de tipos (type narrowing) de los union types dentro de los condicionales:

function process(value: string | number) {
  if (typeof value === "string") {
    // TypeScript knows value is string here
    return value.toUpperCase();
  }
  // TypeScript knows value is number here
  return value.toFixed(2);
}

El narrowing también funciona con:

  • El operador in — para comprobar si existe una propiedad
  • instanceof — para comprobar instancias de clases
  • Comprobaciones de igualdad — === y !==
  • Discriminated unions — comprobando una propiedad de etiqueta (tag) común
type Circle = { kind: "circle"; radius: number };
type Square = { kind: "square"; side: number };
type Shape = Circle | Square;

function area(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.side ** 2;
  }
}

Las discriminated unions son uno de los patrones más potentes de TypeScript. La propiedad kind le indica a TypeScript exactamente con qué variante estás trabajando.

Interfaces y type aliases

Una interface describe la forma de un objeto: sus propiedades, sus tipos y si son obligatorias. Un type alias asigna un nombre a cualquier tipo, incluyendo uniones, tuplas, primitivos y tipos de función.

// Interface
interface User {
  id: number;
  name: string;
  email: string;
}

// Type alias with the same shape
type User = {
  id: number;
  name: string;
  email: string;
};

Para formas de objetos simples, ambos son intercambiables. Las diferencias aparecen cuando compones o extiendes tipos.

Propiedades opcionales y readonly

interface User {
  id: number;
  name: string;
  email?: string;      // optional — string | undefined
  readonly createdAt: Date;
}

const user: User = { id: 1, name: "Ada", createdAt: new Date() };
user.createdAt = new Date(); // Error: cannot assign to a readonly property

Extender interfaces

Las interfaces heredan de otras interfaces mediante extends, pudiendo incluir varias a la vez:

interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

interface User extends Timestamped {
  id: number;
  name: string;
}

Type aliases para uniones, tuplas y funciones

Los type aliases destacan cuando el tipo no es un objeto simple:

type ID = string | number;
type Status = "idle" | "loading" | "success" | "error";
type Pair = [string, number];
type Callback = (data: unknown) => void;

Tipos de intersección

Combina tipos con &:

type User = { id: number; name: string };
type WithTimestamp = { createdAt: Date };

type TimestampedUser = User & WithTimestamp;

¿Interface o type?

Usa interfaces para formas de objetos que puedan ser extendidas o implementadas por clases. Usa type aliases para uniones, intersecciones, tuplas y tipos de función. Elige una convención para las formas de objetos y mantenla de manera consistente.

Genéricos

Los genéricos son parámetros de tipo: permiten que una función, interfaz o clase funcione con cualquier tipo manteniendo la seguridad de tipos. En lugar de definir un tipo fijo o recurrir a any, pasas una variable de tipo que se completa en el momento de la llamada.

function first<T>(items: T[]): T | undefined {
  return items[0];
}

const num = first([1, 2, 3]);    // number
const str = first(["a", "b"]);   // string

Sin genéricos, tendrías que usar any y perder la información del tipo, o escribir una función independiente para cada tipo.

Funciones genéricas

Un parámetro de tipo se declara entre paréntesis angulares. TypeScript generalmente lo infiere a partir de los argumentos, por lo que rara vez es necesario pasarlo explícitamente:

function merge<T, U>(a: T, b: U): T & U {
  return { ...a, ...b };
}

const result = merge({ name: "Ada" }, { age: 36 });
// { name: string; age: number }

Interfaces y clases genéricas

interface Response<T> {
  data: T;
  status: number;
}

interface User {
  id: number;
  name: string;
}

const response: Response<User> = {
  data: { id: 1, name: "Ada" },
  status: 200,
};

class Stack<T> {
  private items: T[] = [];
  push(item: T) { this.items.push(item); }
  pop(): T | undefined { return this.items.pop(); }
}

Restricciones (Constraints)

Usa extends para restringir lo que un genérico puede aceptar:

interface HasId {
  id: number;
}

function findById<T extends HasId>(items: T[], id: number): T | undefined {
  return items.find((item) => item.id === id);
}

Parámetros de tipo por defecto

interface Paginated<T = unknown> {
  data: T[];
  total: number;
  page: number;
}

const response: Paginated = { data: [1, "a"], total: 10, page: 1 };

Los genéricos se eliminan en tiempo de ejecución, por lo que no añaden sobrecarga; existen únicamente para que el compilador y tu editor sean más inteligentes.

El archivo tsconfig.json

El archivo tsconfig.json configura el compilador de TypeScript:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "strict": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

Opciones clave:

  • target — qué versión de JavaScript generar (ES2020 es un buen valor predeterminado)
  • strict — habilita todos los flags de comprobación de tipos estrictos
  • module — el sistema de módulos para la salida (ESNext para proyectos modernos)
  • outDir — dónde se guarda el JavaScript compilado
  • esModuleInterop — permite importaciones por defecto desde módulos CommonJS

Usa siempre "strict": true para proyectos nuevos. Detecta más errores a cambio de requerir un código ligeramente más explícito.

Instalando TypeScript

# As a project dependency (recommended)
npm install --save-dev typescript

# Globally (not recommended for teams)
npm install -g typescript

Compila tu proyecto:

# Using npx
npx tsc

# Or add a script to package.json
npm run build

Para desarrollo con modo watch:

npx tsc --watch

La mayoría de los frameworks modernos (Next.js, Vite, Astro) gestionan la compilación de TypeScript por ti; no es necesario ejecutar tsc manualmente.

Errores comunes

  • Usar any en todas partes, lo que desactiva la comprobación de tipos.
  • Anotar todo cuando la inferencia ya conoce el tipo.
  • No activar el modo estricto en el tsconfig.
  • Confundir los tipos de TypeScript con valores en tiempo de ejecución; los tipos se eliminan en tiempo de compilación.
  • Importar tipos incorrectamente: utiliza import type para importaciones exclusivas de tipos.
  • Ignorar los errores del compilador en lugar de solucionarlos.
  • No instalar los paquetes @types para las librerías de JavaScript.
  • Hacer que todas las propiedades de una interfaz sean opcionales, lo que oculta las comprobaciones obligatorias.
  • Usar any en lugar de un genérico, descartando la información de tipo que querías conservar.

Mejores prácticas

  • Activa el modo estricto desde el primer día.
  • Deja que la inferencia haga el trabajo; anota los tipos solo cuando aporte valor.
  • Usa unknown en lugar de any cuando no conozcas el tipo.
  • Prefiere las interfaces para formas de objetos que puedan extenderse.
  • Usa uniones discriminadas para máquinas de estado y datos variantes.
  • Mantén las definiciones de tipos cerca de donde se utilizan.
  • Usa import type para importaciones solo de tipos y así mantener el código de ejecución ligero.
  • Trata al compilador como a un compañero de equipo, no como un obstáculo.

Qué aprender a continuación

Ya dominas los fundamentos de TypeScript: anotaciones, inferencia, uniones, narrowing, interfaces, type aliases y genéricos. A partir de aquí, el siguiente paso natural son los Advanced Types para aprender sobre utility types, conditional types y mapped types. Elige un proyecto, refactoriza algo de código y deja que la práctica haga el resto.

Anotaciones de tipo

Las anotaciones explícitas ayudan cuando el tipo no es obvio. Deja que la inferencia haga el trabajo cuando sí lo sea.

Preferir
const count = 0;
const items: string[] = [];

function add(a: number, b: number) {
  return a + b;
}
Evitar
const count: number = 0;
const items: Array<string> = [];

function add(a: number, b: number): number {
  return a + b;
}

Uso de any

any desactiva la comprobación de tipos. Usa unknown cuando no conozcas el tipo y luego aplícale narrowing.

Preferir
function parse(input: unknown) {
  if (typeof input === "string") {
    return input.toUpperCase();
  }
  return String(input);
}
Evitar
function parse(input: any) {
  return input.toUpperCase();
}

Preguntas frecuentes

Preguntas frecuentes

Keep learning

Related topics from the roadmap.

$ comienza a aprender

Listo para aprender TypeScript?

Nuestro tutorial interactivo te guia a traves de TypeScript paso a paso — con quizzes y codigo real que puedes ejecutar en el navegador.