Apariencia
💡 Conceptos — TypeScript
Conceptos clave explicados con mis propias palabras.
🅰️ Variables y tipos base
En TypeScript declaro una variable con let y le anoto de qué tipo es con : tipo:
typescript
let nombre: string = "Styp"; // texto
let edad: number = 30; // números (enteros o decimales)
let esDesarrollador: boolean = true; // verdadero / falsoEl : tipo es una promesa al compilador: "esta variable solo guardará ese tipo de dato". Si intento meter otra cosa (edad = "treinta"), TypeScript me avisa antes de ejecutar. Eso es lo que JavaScript normal NO hace.
| Tipo | Qué guarda | Ejemplo |
|---|---|---|
string | Texto | "hola", "Styp" |
number | Números (enteros y decimales) | 30, 3.14 |
boolean | Verdadero o falso | true, false |
🧪 Entrevista: ¿Qué aporta TypeScript sobre JavaScript? Los tipos: detecta errores en tiempo de desarrollo (antes de correr), da autocompletado y documenta el código. Al compilar, los tipos desaparecen y queda JS normal.
🅰️ El tipo any (Clase 2)
any = "cualquier cosa". Una variable any acepta cualquier tipo y TypeScript deja de revisarla (es como apagar TS para esa variable).
typescript
let variableAny: any;
variableAny = "Hola, mundo!"; // ✅ string
variableAny = 42; // ✅ number (también válido)
variableAny = true; // ✅ boolean (también válido)| Tipo | ¿TS lo revisa? | Acepta |
|---|---|---|
string / number / boolean | ✅ Sí | Solo ese tipo |
any | ❌ No | Cualquier cosa |
⚠️ Evita
any: anula la ventaja de TS (detectar errores de tipo). Úsalo solo en casos puntuales. Alternativa segura:unknown(acepta todo pero obliga a comprobar el tipo antes de usarlo). → ver Clase 2
🔒 unknown, void y never (Clase 2)
Otros tipos "diferentes" (detalle en Clase 2):
| Tipo | Qué es | Ejemplo |
|---|---|---|
unknown | Como any, pero obliga a comprobar el tipo antes de usarlo (con typeof). Es el any seguro. | let x: unknown |
void | Tipo de retorno de una función que no devuelve nada | function f(): void {} |
never | Tipo de retorno de una función que nunca termina (siempre lanza error) | function f(): never { throw ... } |
typescript
let x: unknown = 42;
if (typeof x === "number") { // typeof = compruebo el tipo en ejecución
console.log(x + 1); // aquí TS ya sabe que es number
}🧪 Entrevista:
anyvsunknown→ los dos aceptan todo, perounknownes seguro (obliga a verificar el tipo).voidvsnever→voidtermina sin devolver valor;neverno llega a terminar (rompe con error).
🕳️ undefined vs null
Ambos representan "vacío", pero no son lo mismo:
typescript
let mes: string | undefined; // declarada SIN valor → vale undefined solo
let variableNull: null = null; // vacío puesto A PROPÓSITO por mí| Valor | Qué significa | Quién lo pone |
|---|---|---|
undefined | "Sin definir": declaré la variable pero no le di valor. | Lo pone JavaScript solo |
null | "Vacío intencional": aquí no hay nada, y lo decidí yo. | Lo pongo yo a mano |
⚠️ Una variable sin valor sí se puede imprimir: sale la palabra
undefined, no queda en blanco.console.log(mes)→undefined. Eso NO es un error.
🧩 Union types (string | undefined)
El símbolo | significa "o". string | undefined = "esta variable puede ser un texto o estar sin definir".
typescript
let mes: string | undefined; // puede ser "Julio" o undefined
mes = "Julio"; // ✅ válido
mes = undefined; // ✅ válido
mes = 30; // ❌ error: number no está en la unión🔤 Concatenación vs Template strings
Dos formas de armar un texto con variables dentro:
typescript
// 1) Concatenación con + (la vieja escuela)
console.log("Hola, " + nombre + "!");
// 2) Template string con backticks ` ` y ${...} (más limpia)
console.log(`Hola, ${nombre}!`);Las dos imprimen Hola, Styp!. La segunda usa backticks (`, no comillas normales) y ${variable} para insertar valores. Es más legible, sobre todo con varias variables.
💡 El backtick está en el teclado normalmente arriba a la izquierda (tecla junto al
1).${...}solo funciona dentro de backticks, no dentro de"..."ni'...'.
📚 Arrays, tuplas, interfaces y enums (Clase 3)
Resumen (detalle en Clase 3):
| Concepto | Cómo se escribe | Qué es |
|---|---|---|
| Array | let x: number[] = [1,2,3] | Lista de UN mismo tipo, tamaño variable |
| Array mixto | (string | number)[] | Lista donde cada elemento puede ser varios tipos |
| Tupla | [string, number] | Lista de tamaño fijo, un tipo por posición |
| Array de tuplas | [string, number][] | Lista de tuplas |
| Interface | interface P { nombre: string } | Molde que define la forma de un objeto |
| Array de objetos | let ps: P[] = [...] | Lista de objetos que cumplen la interface |
| Enum | enum Dia { Lunes, Martes } | Nombres para un conjunto de valores (numéricos por defecto, desde 0) |
typescript
interface Persona { nombre: string; edad: number; }
let personas: Persona[] = [{ nombre: "Juan", edad: 25 }];
enum Dia { Lunes, Martes, Miercoles } // 0, 1, 2
let d: Dia = Dia.Miercoles;
console.log(d); // 2 (nombre → número)
console.log(Dia[d]); // Miercoles (número → nombre, "reverse mapping")⚠️ Una tupla NO es 100% inmutable (TS permite reasignar y
push); lo fijo es su estructura. Para inmutabilidad real:readonly [string, number].
🧪 Entrevista:
interfacedescribe la forma de un objeto (contrato).enumreemplaza "números mágicos" por nombres legibles. Un enum numérico tiene mapeo inverso (número → nombre).
🧩 Funciones / métodos (Clase 4)
Se tipan los parámetros y el valor de retorno (detalle en Clase 4):
typescript
function sumar(a: number, b: number): number { // params tipados : tipo de retorno
return a + b;
}
function imprimir(msg: string): void { // void = no devuelve nada
console.log(msg);
}| Escritura | Qué es |
|---|---|
(a: number, b: number) | Parámetros obligatorios con su tipo |
: number / : void | Tipo que devuelve (void = nada) |
saludo?: string | Parámetro opcional (puede faltar → undefined) |
saludo: string = "Hola" | Parámetro por defecto (usa ese valor si falta) |
...numeros: number[] | Parámetro rest: junta N argumentos en un array (va al final) |
const f = (a: number) => a * 2 | Arrow function (forma corta) |
function f(a: string): string;function f(a: number): number; | Sobrecarga: varias firmas + una implementación (con typeof distingue casos) |
📝
?vs!:?hace un parámetro opcional.!NO es marcador de parámetro (da error de sintaxis); es el operador non-null assertion sobre un valor (x!.length= "confía, no es null/undefined"). Los parámetros ya son obligatorios por defecto.
⚠️ Los parámetros opcionales van al final, después de los obligatorios.
🧱 Interfaces avanzadas (Clase 5)
Además del uso básico (ver tabla de Clase 3), una interface admite (detalle en Clase 5):
typescript
interface Persona {
readonly nombre: string; // solo asignable una vez
edad: number;
lenguajeFavorito?: string; // opcional
}
interface Empleado extends Persona { // hereda todo Persona + agrega lo suyo
readonly puesto: string;
}
interface Sumar { // interface para la FIRMA de una función
(a: number, b: number): number;
}| Escritura | Qué es |
|---|---|
lenguajeFavorito?: string | Propiedad opcional (puede faltar) |
readonly nombre: string | Propiedad de solo lectura: reasignarla es error TS2540 |
interface B extends A { ... } | B hereda las propiedades de A y suma las suyas |
interface F { (a: T): R } | Interface que describe la firma de una función |
📝
readonly≠const:constbloquea reasignar la variable;readonlybloquea reasignar una propiedad del objeto. Ambos son chequeos de TS que desaparecen al compilar.
🔐 Modificadores de acceso (Clase 7)
Los modificadores de acceso controlan desde dónde se puede usar una propiedad o método de una clase. Son clave para el encapsulamiento.
typescript
class Persona {
public name: string;
protected age: number;
private isDeveloper: boolean;
constructor(name: string, age: number, isDeveloper: boolean) {
this.name = name;
this.age = age;
this.isDeveloper = isDeveloper;
}
public saludar(): string {
return `Hola, me llamo ${this.name}`;
}
protected obtenerEdad(): number {
return this.age;
}
private esDesarrollador(): boolean {
return this.isDeveloper;
}
}| Modificador | Desde fuera de la clase | Uso típico |
|---|---|---|
public | ✅ Sí | API visible de la clase |
protected | ❌ No | Mecánicas internas no visibles externamente |
private | ❌ No | Datos y lógica totalmente interna |
typescript
const persona = new Persona("Juan", 30, true);
console.log(persona.name); // ✅
console.log(persona.saludar()); // ✅
// console.log(persona.age); // ❌ protected
// console.log(persona.obtenerEdad()); // ❌ protected
// console.log(persona.isDeveloper); // ❌ private💡 Regla útil: empieza con lo más restrictivo posible y solo haz público lo que de verdad necesites exponer.
🧬 Herencia (extends) (Clase 8)
La herencia permite crear una clase nueva a partir de una clase base, reutilizando lo que ya existe y agregando solo lo nuevo.
typescript
class Persona {
public nombre: string;
constructor(nombre: string) {
this.nombre = nombre;
}
public saludar(): string {
return `Hola, soy ${this.nombre}`;
}
}
class Empleado extends Persona {
public puesto: string;
constructor(nombre: string, puesto: string) {
super(nombre);
this.puesto = puesto;
}
}| Concepto | Qué hace |
|---|---|
extends | Hace que una clase herede de otra |
super(...) | Llama al constructor de la clase padre |
| Clase base | La clase original que aporta propiedades y métodos |
| Clase hija | La clase que hereda y además agrega o redefine comportamiento |
typescript
class Empleado extends Persona {
public saludar(): string {
return `Hola, trabajo como ${this.puesto}`;
}
}💡 La herencia evita duplicar código. Tomas una base que ya funciona y construyes una versión más específica encima.
🧬 Genéricos (<T>) (Clase 9)
Un genérico es un parámetro de tipo: permite escribir funciones y clases reutilizables para cualquier tipo, sin perder el chequeo de tipos (a diferencia de any, que lo apaga).
typescript
function identity<T>(arg: T): T {
return arg;
}
let a = identity<string>("Hola"); // tipo explícito
let b = identity(42); // TS infiere T = number
class Caja<T> {
private contenido: T;
constructor(contenido: T) {
this.contenido = contenido;
}
public obtenerContenido(): T {
return this.contenido;
}
}
const caja = new Caja<string>("Libros"); // queda fija a T = string
// Interfaz genérica: describe un CONTRATO, sin fijar el tipo todavía
interface Contenedor<T> {
contenido: T;
obtenerContenido(): T;
}
class CajaGenerica<T> implements Contenedor<T> {
contenido: T;
constructor(valor: T) { this.contenido = valor; }
obtenerContenido(): T { return this.contenido; }
}
// La función solo pide "algo que cumpla Contenedor<T>", no una clase concreta
function mostrarContenido<T>(c: Contenedor<T>): T {
return c.obtenerContenido();
}| Concepto | Qué es |
|---|---|
<T> | Parámetro de tipo: "lo que sea que reciba, eso mismo relaciono" |
| Tipo explícito | identity<string>(...) — fijas T a mano |
| Inferencia | identity(42) — TS deduce T del argumento |
class Caja<T> | Genérico aplicado a una clase entera (propiedades, constructor, métodos) |
interface Contenedor<T> | Genérico aplicado a un contrato: cualquier clase que lo implemente puede tener un T distinto |
T extends Forma | Restringe qué tipos puede tomar T (debe cumplir esa forma) |
| Necesito... | Uso |
|---|---|
| Utilidad reutilizable sin estado | Función genérica |
| Entidad con estado (guarda datos) | Clase genérica |
| Contrato que varias clases pueden cumplir | Interfaz genérica |
🧪 Entrevista:
anyvs genérico →anyacepta todo y deja de verificar tipos; un genérico acepta cualquier tipo pero mantiene la relación de tipos entre entrada y salida. LaTes solo convención (de "Type"), no palabra reservada.
⚠️ Una instancia genérica queda fija al tipo con que se creó:
new Caja<string>(...)no puede luego guardar unnumber(TypeScript lo marca conTS2322).
🧩 Extender métodos nativos (Clase 10)
Dos técnicas distintas, aunque ambas se llaman "extender":
typescript
// 1) Restringir un genérico a una FORMA (lo que enseña esta clase)
function obtenerLongitud<T extends {length: number}>(obj: T): number {
return obj.length; // T puede ser string, array, o cualquier cosa con .length
}
// 2) Declaration merging + prototype: agregar un método NUEVO de verdad (bonus)
interface String {
invertir(): string;
}
String.prototype.invertir = function (): string {
return this.split("").reverse().join("");
};| Técnica | Qué hace | Riesgo si te saltas un paso |
|---|---|---|
T extends {propiedad} | Restringe qué tipos acepta un genérico, para poder usar esa propiedad con seguridad | Si el tipo no cumple, error de compilación (TS2345) — no llega a ejecutarse |
Declaration merging + .prototype | Le agrega un método real y nuevo a un tipo nativo (String, Array, etc.) | Si solo declaras la interface y olvidas el .prototype, compila bien pero truena en ejecución |
⚠️
typeofsobre un array da"object", no"array"— para distinguir arrays de verdad se usaArray.isArray(valor).
📦 Módulos: export / import (Clase 11)
Dividir el código en archivos pequeños (módulos) en vez de un solo archivo gigante (monolito). export expone algo hacia afuera; import { } lo trae a otro archivo.
typescript
// calculator.ts
export function suma(a: number, b: number): number { return a + b; }
export const Pi = 3.1416;
// main.ts
import { suma, Pi } from "./calculator.js"; // .js al final, no .ts
console.log(suma(Pi, 1));| Concepto | Qué hace |
|---|---|
export function / export const | Marca algo como visible fuera de su archivo |
import { X } from "./ruta.js" | Trae elementos específicos de otro archivo (llaves = puntual) |
import X from "./ruta.js" (sin llaves) | Importa un export default (uno solo por archivo) |
tsc main.ts | Compila main.ts y, en cascada, todo lo que main.ts importe |
⚠️ Gotcha de proyectos con
"type": "module"enpackage.json(como este): el import necesita la extensión.jscompleta (from "./calculator.js", nofrom "./calculator"). TypeScript compila igual sin ella, pero Node truena al ejecutar (ERR_MODULE_NOT_FOUND) porque ESM no adivina la ruta como CommonJS.
🧪 Entrevista: ¿Qué es un monolito? Un archivo que concentra toda la lógica — difícil de mantener, escalar y depurar. Los módulos son la solución: piezas pequeñas, reutilizables, con una responsabilidad clara cada una.
🌐 TypeScript compilado y ejecutado en el navegador (Clase 12)
El navegador solo entiende JavaScript. Un archivo .ts necesita compilarse con tsc a .js antes de que un <script src="..."> pueda invocarlo — nunca se apunta al .ts directamente.
typescript
// main.ts
console.log("Hola desde mi explorador");html
<!-- index.html -->
<script src="main.js"></script> <!-- .js, NO .ts -->bash
tsc main.ts # genera main.js| Concepto | Qué es |
|---|---|
tsc archivo.ts | Compila a .js — es lo único que el navegador puede ejecutar |
| Live Server / Live Preview | Extensión de VS Code que sirve el HTML en localhost con recarga automática |
DevTools Console (F12) | Donde se confirma que el console.log llegó al navegador |
document.getElementById(...) as HTMLInputElement | Cast necesario porque el DOM no sabe de antemano qué tipo exacto de elemento devolvió |
<script type="module"> | Obligatorio para que el navegador entienda import/export (Clase 11) directamente — sin él: SyntaxError: Cannot use import statement outside a module |
⚠️ Gotcha #1: editar
main.tssin volver a corrertscdeja el sitio mostrando la versión vieja — el<script>sigue apuntando al.jsque no cambió.
⚠️ Gotcha #2: sin módulos, varios
<script>comparten un solo scope global y se ejecutan en el orden físico en que aparecen en el HTML — invertir el orden entre quien declara una variable y quien la usa lanzaReferenceError.
🗂️ Organizar un proyecto con src/public y outDir (Clase 13)
Separar código fuente (src/, no se despliega) de archivos públicos (public/, sí se despliega). outDir en tsconfig.json le dice a tsc dónde dejar el .js compilado — resuelto relativo al tsconfig.json, no a tu terminal.
json
// src/tsconfig.json
{ "compilerOptions": { "outDir": "../public/scripts", "target": "ES6", "module": "CommonJS", "strict": true } }| Comando | Desde dónde funciona |
|---|---|
tsc (sin nada) | Solo parado en la carpeta del tsconfig.json |
tsc -p ruta/tsconfig.json | Desde cualquier carpeta |
tsc archivo.ts | Ignora el tsconfig.json — o truena con TS5112 si hay uno presente |
⚠️
outDirclona la estructura desrc/: una subcarpetasrc/helpers/x.tstermina enoutDir/helpers/x.js, no todo aplanado.
⚠️
module: "CommonJS"+import/exportno corre en el navegador, ni contype="module"— se compila arequire()/exports(pensado para Node), y el navegador no tienerequire(). La solución:module: "ES6"+type="module"en el<script>, o un bundler.
🖱️ Manipular el DOM: querySelector + tipos (Clase 14)
document.querySelector(selector) siempre devuelve Element | null, sin importar el selector — dos problemas en un solo tipo: puede ser null (se resuelve con ?.) y es demasiado genérico (se resuelve con as TipoEspecífico). Son soluciones distintas para problemas distintos; hace falta ambas para acceder a propiedades como .value o .placeholder sin error de compilación.
typescript
const message = document.querySelector('#message') as HTMLInputElement;
console.log(message?.placeholder);| Situación | Errores de tsc |
|---|---|
Sin ?. ni as | TS18047 (posible null) y TS2339 (propiedad no existe en Element) |
Solo ?. | Solo TS2339 (el tipo sigue siendo genérico) |
?. + as Tipo | Compila limpio |
⚠️
HTMLHeadElement≠HTMLHeadingElement. El primero es para<head>; el segundo es el correcto para<h1>–<h6>.tscno avisa si los confundes (ambos derivan deHTMLElement) — hay que revisar el nombre con cuidado.
💡
tsc archivo.ts --watch(o-w) recompila solo cada vez que guardas, sin corrertsca mano en cada cambio — deja el proceso corriendo hastaCtrl+C.
⚛️ Proyecto React con Vite (Clase 15)
npm create vite@latest genera un proyecto React + TypeScript completo (JSX en archivos .tsx) en un solo comando. El tsconfig.json ya no configura nada directamente — solo referencia a tsconfig.app.json (código de src/, corre en el navegador) y tsconfig.node.json (vite.config.ts, corre en Node).
bash
npm create vite@latest mi-app -- --template react-ts
cd mi-app && npm install && npm run dev⚠️
npm run devNO revisa tipos — solo transpila (Babel/SWC quitan los tipos sin validarlos). La verificación de tipos real ocurre ennpm run build("build": "tsc -b && vite build") — un error de tipos no te detiene mientras desarrollas, pero sí al compilar para producción.
💡 HMR preserva el estado: a diferencia de una recarga completa (Live Server, Clase 12), el Hot Module Replacement de Vite reemplaza solo el módulo cambiado — un contador de React sigue en su valor tras guardar.
📝 El tooling cambia con el tiempo: la opción "TypeScript + SWC" del asistente ya no existe en versiones recientes de
create-vite— hoy la lista muestra "TypeScript" y "TypeScript + React Compiler" en su lugar. El paquete@vitejs/plugin-react-swcsigue en npm, pero hay que configurarlo a mano.
⚠️ Bug real verificado:
vite ^8.2.0(Rolldown-Vite, experimental — depende derolldown) combinado con React Compiler rompe el HMR conRefreshRuntime.register is not a function(pantalla en negro). Solución: usarvite ^7.x(clásico,esbuild+rollup) sin el plugin de React Compiler. Historia completa, diagnóstico y fix en la Clase 15.
🅰️ Angular con TypeScript: decoradores y standalone components (Clase 16)
ng new → ng serve (puerto 4200) genera un proyecto Angular + TypeScript ya integrado — sin configurar nada extra. El decorador @Component({...}) marca una clase como componente visual; hoy Angular usa standalone components (cada uno declara sus propios imports, sin NgModule) y main.ts arranca con bootstrapApplication, no con un AppModule.
typescript
@Component({ selector: 'app-root', imports: [RouterOutlet], templateUrl: './app.html', styleUrl: './app.css' })
export class App {
protected readonly title = signal('proyectoAngular'); // signal reactiva
}⚠️
experimentalDecorators: trueya NO es estrictamente obligatorio — verificado: TypeScript 6.0 compila@Componentcon el nuevo estándar de decoradores de ECMAScript (helpers__esDecorate/Symbol.metadata) sin esa bandera. Angular la sigue incluyendo por defecto (compatibilidad con su sistema de inyección de dependencias), pero no es un requisito duro para que compile.
💡
ng serveSÍ revisa tipos en cada guardado (compilación AOT real) — al revés quenpm run devde Vite (Clase 15), que solo transpila sin validar. Un error de tipos hace fallar el bundle de Angular al instante, no solo en el build de producción.
🐛 TypeScript 6.0 exige
rootDirexplícito en cuanto hayoutDir— antes lo inferia solo. Angular CLI 22 generatsconfig.app.json/tsconfig.spec.jsonsinrootDir, lo que produceerror TS5011. Fix: agregar"rootDir": "./src"a ambos archivos.
🌐 API con TypeScript y Express (Clase 17)
npm init -y + npm install express cors dotenv + typescript/ts-node/@types/* como dev — un middleware (app.use(...)) es una función que corre entre la petición y la respuesta (cors() agrega headers, express.json() parsea el body a req.body).
typescript
import express from 'express'; // requiere esModuleInterop: true (paquete CommonJS)
const app = express();
app.use(express.json());
app.get('/hello', (req, res) => res.send('Hola'));
app.listen(3000);⚠️
npm installcon varios paquetes es todo-o-nada: un solo typo en el nombre de un paquete (@type/nodeen vez de@types/node) aborta el comando completo conE404— ninguno de los paquetes bien escritos queda instalado.
🐛
npm install typescriptsin fijar versión es impredecible: una vez resolvió7.0.2(rompióts-nodeconTypeError: Cannot read properties of undefined) y, al reinstalar el mismo proyecto, resolvió5.9.3(todo funcionó sin tocar nada). Fija la versión (typescript@5.9.3) para reproducibilidad —tsxes además un reemplazo moderno dets-nodesi te toca una combinación incompatible.
🐛 El mismo
TS5011derootDir(ver Clase 16) también aparece aquí: sinrootDirexplícito junto aoutDir, el build falla y además el archivo compilado queda en la ruta equivocada (dist/src/server.jsen vez dedist/server.js).
CRUD real + SQLite (node:sqlite): Node.js 22.5+ trae un cliente de SQLite integrado — sin instalar ningún paquete. Un prepared statement (? como placeholder) separa la consulta de los datos, y por eso previene inyección SQL:
typescript
import { DatabaseSync } from 'node:sqlite';
const db = new DatabaseSync('./data/nexus.db');
db.prepare('SELECT * FROM empleados WHERE id = ?').get(req.params.id);⚠️ Rutas fijas antes que dinámicas:
app.get('/empleados/:id', ...)registrada ANTES queapp.get('/empleados/estadisticas', ...)hace que/empleados/estadisticasse interprete comoid = "estadisticas"— Express prueba las rutas en el orden en que las registraste, no por especificidad.
📎 Relacionado: Comandos · Clase 3 · Clase 4 · Clase 5 · Clase 7 · Clase 8 · Clase 9 · Clase 10 · Clase 11 · Clase 12 · Clase 13 · Clase 14 · Clase 15 · Clase 16 · Clase 17 · Errores