Use this file to discover all available pages before exploring further.
Esta es una referencia rápida de los conceptos de TypeScript cubiertos en el módulo 06 de JSCamp, organizada por tema. Expande cada sección para ver ejemplos de código extraídos directamente de los archivos del bootcamp. Úsala como cheatsheet mientras trabajas en los módulos 06, 07 y 08.
TypeScript es un superset de JavaScript. Todo el código JS válido también es TS válido. El compilador te avisa de los errores antes de ejecutar el código.
Tipos Primitivos y Básicos
TypeScript añade anotaciones de tipo sobre los primitivos de JavaScript. Puedes declarar tipos explícitamente o dejar que TypeScript los infiera automáticamente.
// ── Inferencia automática ──────────────────────────const nombre = "midudev" // inferido: stringconst ciudad = "Madrid" // inferido como literal: "Madrid" (const)let pais = "España" // inferido: string (let → amplio)// ── Anotaciones explícitas ─────────────────────────const vacio: string = ""let isActive: boolean = truelet age: number | null = null // unión con null// ── Otros primitivos ──────────────────────────────const numeroGrande: bigint = 9007199254741991nconst id: symbol = Symbol("id")// ── Literales como tipos ──────────────────────────type Direction = 'up' | 'down' | 'left' | 'right'type Level = 1 | 2 | 3 | 4 | 5// ── Type aliases ──────────────────────────────────type UserId = { readonly id: string | number}type User = { readonly name: string readonly age: number email?: string // propiedad opcional company?: Company // propiedad opcional anidada role: "admin" | "user" | "editor"}// ── Intersección de tipos ─────────────────────────type UserWithBirthdate = { birthdate: Date }type UserEntity = User & UserId & UserWithBirthdate
Con const, TypeScript infiere el tipo literal más estrecho ("Madrid"). Con let, infiere el tipo amplio (string). Usa as const para forzar inferencia literal en let.
Arrays y Tuplas
Arrays almacenan colecciones de elementos del mismo tipo. Tuplas son arrays de longitud fija con tipos específicos por posición.
Estos cuatro tipos especiales controlan el comportamiento del sistema de tipos en casos límite.
// ── ANY: desactiva la comprobación de tipos ───────// ❌ Evitar siempre que sea posiblelet cualquierCosa: any = "hola"cualquierCosa = 42cualquierCosa = trueconst result = cualquierCosa + 8 // no hay error, pero es peligroso// ── UNKNOWN: alternativa segura a any ────────────// ✅ Acepta cualquier valor pero obliga a verificar antes de usarlet valorDesconocido: unknown = "hola"valorDesconocido = 42// Requiere type narrowing para usarlo:if (typeof valorDesconocido === 'number') { const resultadoSeguro = valorDesconocido + 8 // ✅ OK}if (typeof valorDesconocido === 'string') { console.log(valorDesconocido.toUpperCase()) // ✅ OK}// ── VOID: funciones que no retornan valor útil ───function saludar(): void { console.log("Hola!")}function logError(errorMessage: string): void { if (errorMessage.length === 0) return undefined console.error("Error:", errorMessage)}// ── NEVER: el tipo imposible ─────────────────────// Para funciones que nunca terminan o siempre lanzan errorfunction bucleInfinito(): never { while (true) { /* ... */ }}function throwError(message: string): never { throw new Error(message)}// NEVER en exhaustiveness checking (discriminated unions):function revisarValor(x: number | string) { if (typeof x === 'number') { console.log("Número:", x) } else if (typeof x === 'string') { console.log("String:", x) } else { // Aquí x es de tipo 'never': hemos cubierto todos los casos throwError("Tipo no soportado") }}
Tipo
Acepta
Permite usar sin verificar
Cuándo usarlo
any
Todo
✅ Sí (sin seguridad)
❌ Nunca (migración puntual)
unknown
Todo
❌ No
✅ Datos externos, errores de catch
void
undefined
N/A
✅ Funciones sin retorno
never
Nada
N/A
✅ Funciones que no terminan o lanzan
Funciones
TypeScript permite tipar parámetros, valores de retorno y hasta el tipo completo de una función.
// ── Parámetros y retorno explícitos ───────────────function sumar(a: number, b: number): number { return a + b}const multiplicar = (a: number, b: number): number => a * bconst dividir = (a: number, b: number): number => a / b// ── Parámetros opcionales (?) ─────────────────────function saludar(nombre: string, apellido?: string): string { if (apellido) return `Hola, ${nombre} ${apellido}` return `Hola, ${nombre}`}// ── Parámetros con valor por defecto ─────────────function crearUsuario(nombre: string, rol: string = "admin") { return { nombre, rol }}// ── Rest parameters ───────────────────────────────function sumarNumeros(...numeros: number[]): number { return numeros.reduce((acc, curr) => acc + curr, 0)}sumarNumeros(1, 2)sumarNumeros(1, 2, 3, 4, 5)// ── Tipo de función (Function Types) ─────────────type OperacionMatematica = (a: number, b: number) => numberconst division: OperacionMatematica = (a, b) => a / bconst resta: OperacionMatematica = (a, b) => a - b// ── Callbacks tipados ─────────────────────────────function procesarArray( arr: number[], callback: (item: number) => string): string[] { return arr.map(callback)}// ── Overloads ─────────────────────────────────────function formatId(id: number): stringfunction formatId(id: string): stringfunction formatId(id: number | string): string { return `ID-${id}`}
Type Narrowing
El type narrowing (estrechamiento de tipos) permite reducir un tipo amplio a uno más específico usando comprobaciones en tiempo de ejecución. TypeScript entiende estas comprobaciones y ajusta el tipo automáticamente dentro de cada rama.
// ── typeof narrowing ──────────────────────────────function procesar(valor: number | string) { if (typeof valor === 'number') { // valor: number aquí console.log(valor.toFixed(2)) } else { // valor: string aquí console.log(valor.toUpperCase()) }}// ── Nullish narrowing ─────────────────────────────function imprimirMensaje(mensaje: string | null | undefined) { if (mensaje) { console.log(mensaje.toUpperCase()) // mensaje: string (no null/undefined) }}// ── in narrowing (discriminated union) ───────────type Pez = { nadar: () => void; nombre: string }type Pajaro = { volar: () => void; nombre: string }type Perro = { ladrar: () => void; nombre: string }type Animal = Pez | Pajaro | Perrofunction moverAnimal(animal: Animal) { if ('nadar' in animal) { animal.nadar() // animal: Pez } else if ('volar' in animal) { animal.volar() // animal: Pajaro } else { animal.ladrar() // animal: Perro }}// ── instanceof narrowing ──────────────────────────function formatDate(value: Date | string): string { if (value instanceof Date) { return value.toUTCString() // value: Date } return new Date(value).toUTCString() // value: string}// ── Discriminated unions con literal type ─────────type JobData = { modality: "remote" | "onsite" | "hybrid" level: "junior" | "mid" | "senior" technology: string[]}function describir(modality: JobData["modality"]): string { switch (modality) { case "remote": return "100% remoto" case "onsite": return "Presencial" case "hybrid": return "Híbrido" }}
Los discriminated unions combinan un campo literal discriminador (p. ej. type: "success" | "error") con datos específicos por variante. Son la alternativa tipada a los instanceof con clases.
Interfaces vs Types
Tanto interface como type definen la forma de un objeto. La elección depende del caso de uso.
// ══════════════════════════════════════════════════// INTERFACES// ══════════════════════════════════════════════════interface Persona { readonly name: string readonly age: number}interface Identificable { id: `user-${number}` // template literal type}// extends: herencia entre interfacesinterface User extends Persona, Identificable { email?: string role: "admin" | "user" | "editor" saludar: () => string login(): boolean}interface Admin extends User { adminLevel: number accessAllAreas: boolean}// Declaration merging (solo interfaces):// TypeScript fusiona las dos declaraciones automáticamenteinterface Hero { nombre: string }interface Hero { poder: string }const hero: Hero = { nombre: "Superman", poder: "Volar" } // ✅// Interfaces para clases (implements)interface MediaPlayer { play(): void pause(): void stop(): void}class Reproductor implements MediaPlayer { play(): void { console.log("Reproduciendo...") } pause(): void { console.log("Pausado") } stop(): void { console.log("Detenido") }}// ══════════════════════════════════════════════════// TYPE ALIASES// ══════════════════════════════════════════════════// Uniones: solo posibles con typetype StringOrNumber = string | numbertype ID = string | number | symbol// Tipos literalestype Rol = "admin" | "user" | "editor"// Intersección (equivalente a extends)type UserEntity = User & Identificable & { birthdate: Date }// Tipos de utilidad con typetype Configuration = { readonly apiKey: string readonly theme: 'dark' | 'light'}
Diferencias clave
Característica
interface
type
Declaration merging
✅
❌
Uniones (|)
❌
✅
Intersecciones (&)
via extends
✅ directo
Tipos primitivos/tuplas
❌
✅
implements en clases
✅
✅
Mapped types
❌
✅
Regla general: usa interface para definir la forma de objetos y contratos de clases; usa type para uniones, intersecciones, tipos de función y alias de primitivos.
Generics
Los generics permiten escribir código reutilizable que funciona con cualquier tipo, manteniendo la seguridad de tipos.
// ── Generic básico ────────────────────────────────function identity<T>(value: T): T { return value}identity<string>("hola") // T = stringidentity<number>(42) // T = numberidentity("inferido") // T inferido como string// ── Generic con arrays ────────────────────────────function firstElement<T>(arr: T[]): T | undefined { return arr[0]}const first = firstElement([1, 2, 3]) // number | undefined// ── Generic con múltiples parámetros ─────────────function pair<K, V>(key: K, value: V): [K, V] { return [key, value]}const p = pair("id", 42) // [string, number]// ── Interfaces genéricas ──────────────────────────interface ApiResponse<T> { data: T status: number message: string}interface Repository<T> { getById(id: string): Promise<T> getAll(): Promise<T[]> create(item: Omit<T, "id">): Promise<T> update(id: string, item: Partial<T>): Promise<T> delete(id: string): Promise<void>}// ── Constraints con extends ───────────────────────// T debe tener al menos la propiedad `id`function getById<T extends { id: string }>(items: T[], id: string): T | undefined { return items.find(item => item.id === id)}// ── keyof constraint ─────────────────────────────// K debe ser una clave válida de Tfunction getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]}// Ejemplo con el tipo Job del módulo 08:interface Job { id: string title: string company: string location: string}const job: Job = { id: "1", title: "Dev", company: "Acme", location: "Madrid" }const title = getProperty(job, "title") // string ✅// const bad = getProperty(job, "email") // ❌ Error: "email" no es keyof Job// ── Generic con valor por defecto ─────────────────interface PaginatedResponse<T = unknown> { items: T[] total: number page: number pageSize: number}
Utility Types
TypeScript incluye una librería de utility types que transforman tipos existentes sin tener que reescribirlos. Son fundamentales en el módulo 08 donde se usan Omit, Partial y tipos inferidos de Zod.
// Tipos de partida para los ejemplos:interface Job { id: string title: string company: string location: string description: string}
Tabla de Utility Types
Utility Type
Descripción
Partial<T>
Hace todas las propiedades opcionales
Required<T>
Hace todas las propiedades obligatorias
Readonly<T>
Hace todas las propiedades de solo lectura
Pick<T, K>
Selecciona un subconjunto de propiedades
Omit<T, K>
Excluye un subconjunto de propiedades
Record<K, V>
Crea un tipo objeto con claves K y valores V
Exclude<T, U>
Excluye de T los miembros asignables a U
Extract<T, U>
Extrae de T los miembros asignables a U
NonNullable<T>
Elimina null y undefined de T
ReturnType<F>
Tipo del valor de retorno de una función F
Parameters<F>
Tupla con los tipos de parámetros de una función F
Awaited<T>
Desenvuelve el tipo de una Promise<T>
InstanceType<C>
Tipo de instancia de una clase C
Ejemplos de código
// ── Partial<T> ────────────────────────────────────// Usado en PATCH: todos los campos opcionales para actualización parcialtype UpdateJobDTO = Partial<Job>// Equivale a: { id?: string; title?: string; company?: string; ... }const patch: UpdateJobDTO = { location: "Barcelona" } // ✅ solo un campo// ── Required<T> ───────────────────────────────────interface Config { host?: string; port?: number }type RequiredConfig = Required<Config>// { host: string; port: number } — ya no son opcionales// ── Readonly<T> ───────────────────────────────────const frozenJob: Readonly<Job> = { id: "1", title: "Dev", company: "Acme", location: "Madrid", description: "..." }// frozenJob.title = "Otro" // ❌ Error: no se puede asignar// ── Pick<T, K> ────────────────────────────────────type JobSummary = Pick<Job, "id" | "title" | "company">// { id: string; title: string; company: string }// ── Omit<T, K> ────────────────────────────────────// Usado en el módulo 08 para CreateJobDTO (sin id, lo genera la BD)type CreateJobDTO = Omit<Job, "id">// { title: string; company: string; location: string; description: string }// ── Record<K, V> ─────────────────────────────────type ModalityLabel = Record<"remote" | "onsite" | "hybrid", string>const labels: ModalityLabel = { remote: "Remoto", onsite: "Presencial", hybrid: "Híbrido"}// ── ReturnType<F> ────────────────────────────────async function fetchJobs() { return [{ id: "1", title: "Dev" }]}type FetchJobsResult = Awaited<ReturnType<typeof fetchJobs>>// { id: string; title: string }[]// ── Parameters<F> ────────────────────────────────function createJob(title: string, company: string, level: number) {}type CreateJobParams = Parameters<typeof createJob>// [title: string, company: string, level: number]// ── Exclude y Extract ────────────────────────────type Level = "junior" | "mid" | "senior"type SeniorLevels = Extract<Level, "mid" | "senior"> // "mid" | "senior"type NoSenior = Exclude<Level, "senior"> // "junior" | "mid"// ── NonNullable ───────────────────────────────────type MaybeString = string | null | undefinedtype DefiniteString = NonNullable<MaybeString> // string// ── Ejemplo real del módulo 08 (types.ts) ─────────// CreateJobDTO y UpdateJobDTO se definen exactamente así:interface FullJob { id: string title: string company: string location: string description: string}type CreateFullJobDTO = Omit<FullJob, "id"> // sin idtype UpdateFullJobDTO = Partial<CreateFullJobDTO> // todo opcional
Zod puede inferir utility types automáticamente con z.infer<typeof schema> y schema.partial(). En el módulo 08 no hace falta definir UpdateJobDTO a mano: se obtiene de jobSchema.partial().