©️ Por: Ing. Gary Guzmán
📃 Todos mis resúmenes por @garyDav
🗓️ Publicado, 02 de Junio del 2026
Contenido de la materia
Resumen Typescript
TypeScript es JavaScript con una sintaxis para tipos.

Éste superset de TypeScript es un lenguaje que utiliza como base JavaScript.
Si tenemos código en JavaScript es totalmente compatible con Typescript a no ser que se trate de problemas de compatibilidad por versión oh probar algún feature nuevo de JavaScript.
Lo importante de TypeScript es que no funciona en tiempo de ejecución, eso quiere decir que al final lo que llevaremos al navegador (cliente) es JavaScript plano.
Para probarlo ingresa a: Playground Typescript
¿Por que aprenderlo?
Primeramente por popularidad según la Encuesta 2024
¿Eso que quiere decir?. Sí mas gente lo está usando, pues hay mas trabajos que lo requieren.
TypeScript fue creado por Microsoft por el 2012. Claro que pasaron varios años trabajándolo.
Justamente nació de la necesidad de realizar proyectos mucho mas grandes y fiarnos de JavaScript no era lo mas viable, se necesita un grado de seguridad poder trabajar con este lenguaje. Como tal JavaScript en un lenguaje débil y dinámica.
let a = 'hola'; // string
a = 2; // number
console.log(typeof a); // -> number
Lo que nos dá TypeScript es tener tipos de datos fuertes y estáticos, ya sea añadiendo los tipos de forma explícita o inferida.
TypeScript nos advierte que si realizamos cambios de tipos en el código, pues nos lo marca como un error y ya no ejecuta esto en tiempo de compilación y en tiempo real utilizando algun editor o el playground que vimos antes.
Una de las razones mas relevantes por el porque se utiliza, es por el cambio de tipos de datos que el motor de JavaScript lo realiza, o sea la coerción de tipos.
3 * "3" // 9
1 + "2" + 1 // "121""
(1_ + 2) + "1" // "31""
true + true // 2
10 - true // 9
const foo = {
valueOf: () => 2
}
3 + foo // 5
4 * foo // 8
const bar = {
toString: () => " promise is a boy :)"
}
1 + bar // "1 promise is a boy :)"
4 * [] // 0
4 * [2] // 8
4 + [2] // "42"
4 + [1, 2] // "41,2"
4 * [1, 2] // NaN
4 + [1,2,3] // "41,2,3"
"string" ? 4 : 1 // 4
undefined ? 4 : 1 // 1
"name" + {} // "name[object Object]
//similar to: 4 * [] // 0
4 * Number([].toString())
4 * Number("")
4 * 0
//similar to: 4 / [2] // 2
4 / Number([2].toString())
4 / Number("2")
4 / 2
// Values Falsy: (false, 0, null, undefined, "", NaN, -0)
// Values Truthy: if (-1) if ("0") if ({}) if ([])
🚨 TypeScript no funciona en tiempo de ejecución, solo el tiempo de compilación y aumenta nuestro código final justamente para validar y aplicar las reglas de TypeScript.
Inferencia de tipos, solo utilizarlo para los tipos de datos primitivos o básicos.
function saludar(name) {
console.log(`Hola ${name.toLowerCase()}`); // ❌ la propiedad no existe para 'number'
}
saludar(2);
En este caso el error está en tiempo de ejecución el compilador puede dejarlo pasar pero el error se verá cuando se invoque la función.
function saludar({ name, age }: { name: string; age: number }) {
console.log(`Hola ${name}, tienes ${age} años`);
}
saludar({ name: 'Pepe', age: 22 });
Para tipar funciones, el tipo Function es el any de las funciones:
// const sayHiFromFunction = (fn: Function) => { // En lo posible no utilizar Function
const sayHiFromFunction = (fn: (name: string) => void) => {
// `void` -> No tiene por que devolver nada la función, aunque haya un `return` (no me importa lo que devuelva)
fn('Miguel');
};
const sayHi = (name: string) => {
// Puedes ponerle `pepito` al parámetro `name`
console.log(`Hola ${name}`);
};
sayHiFromFunction(sayHi);
Tipar arrow function:
const sumar = (a: number, b: number): number => {
return a + b;
};
// 🗒️ siempre que puedas evitar tipar la función y puede ser inferido, pues evitarlo
Otra forma de tipar una función arrow:
const restar: (a: number, b: number) => number = (a, b) => {
return a - b;
};
Un tipo para indicar que nunca devolverá nada never:
// - Nunca retorna (por ejemplo, lanza una excepción o entra en un bucle infinito).
// - Maneja casos imposibles o inalcanzables en estructuras de control.
// Funciones que lanzan errores
function throwError(message: string): never {
throw new Error(message);
}
// Funciones con bucles infinitos
function bucleInfinito(): never {
while (true) {
// proceso continuo
}
}
// Verificación de exhaustividad en `switch`
type Rol = 'admin' | 'usuario';
function manejarRol(rol: Rol) {
switch (rol) {
case 'admin':
// lógica para admin
break;
case 'usuario':
// lógica para usuario
break;
default:
const error: never = rol; // TypeScript lanza error si aparece un nuevo valor
}
}
Inferencia de funciones anónimas según el contexto.
// La inferencia a partir de métodos o librerías es gracias a los `genéricos`
const avengers = ['Spidey', 'Hulk', 'Iron Man'];
avengers.forEach(function (avenger) {
// No es necesario añadir tipo
console.log(avenger.toUpperCase()); // Reconoce el autocompletado
});
Para object utilizamos alias type:
type Hero = { // Importante utilizar `PascalCase`
name: string
age: number
}
let hero: Hero {
name: 'Thor',
age: 1500
}
// function createHero(name: string, age: number): Hero { // 01: se puede hacer de esta manera o:
function createHero(hero: Hero): Hero {
const { name, age } = hero
// code ...
return { name, age }
}
// const thor = createHero('Thor', 1500) // 01: para esta opción
const thor = createHero({ name: 'Thor', age: 1500})
Manejando Optional properties:
type Hero = {
readonly id?: number; // Evita la `Mutabilidad`
name: string;
age: number;
isActive?: boolean;
};
let hero: Hero = {
name: 'Thor',
age: 1500,
};
function createHero(hero: Hero): Hero {
const { name, age } = hero;
// code ...
return { name, age, isActive: true };
}
const thor = createHero({ name: 'Thor', age: 1500 });
console.log(thor.id?.toString()); // Los valores opcionales pueden ser `undefined`
thor.id = 923748741037457; // Error: gracias a `readonly`
🚨
readonlyno evita laInmutabilidadsolo nos advierte gracias atypescript. Si quisieramos evitar cambios en nuestraspropertiespodríamos utilizar `Object.freeze({…}) Pero eso ya es otro tema.
Utilizando Template union types:
type HeroId = `${string}-${string}-${string}-${string}-${string}`
type Hero = {
readonly id?: HeroId
name: string
age: number
isActive?: boolean
}
let hero: Hero {
name: 'Thor',
age: 1500
}
function createHero(hero: Hero): Hero {
const { name, age } = hero
// code ...
return {
id: crypto.randomUUID(), // `crypto` es nativo
name,
age,
isActive: true
}
}
const thor = createHero({ name: 'Thor', age: 1500})
console.log(thor)
Otro ejemplo:
type HeadecimalColor = `#${string}`;
const color: HeadecimalColor = '0033ff'; // Error del bueno 😄
const color2: HeadecimalColor = '#0033ff';
🚨 Recuerda que estas “
validaciones” solo es para que salte nuestrolinter.
❗ Si queremos validar realmente utilizar librerías apropieadas.
Utilizando Union types:
type HeroId = `${string}-${string}-${string}-${string}-${string}`
type HeroPowerScale = 'local' | 'planetary' | 'galactic' | 'universal' | 'multiversal'
// let am: number | string
// am = 123 // ✅
// am = '123abc' // ✅
// am = true // ❌
// let amm: string | 2
// amm = 3 // ❌
// am = 2 // ✅
type Hero = {
readonly id?: HeroId
name: string
age: number
isActive?: boolean
powerScale?: HeroPowerScale
}
let hero: Hero {
name: 'Thor',
age: 1500
}
function createHero(hero: Hero): Hero {
const { name, age } = hero
// code ...
return {
id: crypto.randomUUID(),
name,
age,
isActive: true
}
}
const thor = createHero({ name: 'Thor', age: 1500})
// thor.powerScale = 'MUY FUERTE' // ❌
thor.powerScale = 'universal'
Utilizando Intersection Types:
type HeroBasicInfo = {
name: string
age: string
}
type HeroProperties = {
readonly id?: HeroId
isActive?: boolean
powerScale?: HeroPowerScale
}
type Hero = HeroBasicInfo & HeroProperties
let hero: Hero {
name: 'Thor',
age: 1500
}
function createHero(input: HeroBasicInfo): Hero {
const { name, age } = input
// code ...
return {
id: crypto.randomUUID(),
name,
age,
isActive: true
}
}
const thor = createHero({ name: 'Thor', age: 1500})
Utilizando Type Indexing
type HeroProperties = {
isActive: boolean;
address: {
planet: string;
city: string;
};
};
const addressHero: HeroProperties['address'] = {
planet: 'Earth',
city: 'Madrid',
};
Utilizando Type from value
const address = {
planet: 'Earth',
city: 'Madrid',
};
type Address = typeof address; // { planet: string, city: string }
const addressTwitch: Address = {
planet: 'Mars',
city: 'Twitch',
};
Ejemplo con función:
function crearConexion() {
return { host: 'localhost', puerto: 5432 };
}
type Conexion = ReturnType<typeof crearConexion>; // utility `ReturnType`
// Conexion es: { host: string; puerto: number }
⚠️ Limitaciones
- Solo puedes usar
typeofen identificadores (variables, funciones), no en expresiones complejas.- No puedes usarlo para verificar interfaces en tiempo de ejecución
- Otros
utilitiesde TypeScript:typeof,keyof,infer, y algunos adicionales comoReturnType,Parameters,InstanceType,
Utilizando types para Arrays:
// const languages: string[] = []
const languages: (string | number)[] = [];
languages.push('JavaScript');
languages.push('Python');
languages.push(2);
languages.push(true); // ❌
Otro ejemplo:
type CellValue = 'X' | 'O' | '';
// type GameBoy: string[][]
// type GameBoy: Array<string[]>
// type GameBoy: Array<Array<string>>
type GameBoy = [
// Tupla
[CellValue],
[CellValue],
[CellValue],
[CellValue],
[CellValue],
[CellValue],
[CellValue],
[CellValue],
[CellValue],
];
const gameBoy: GameBoy = [
['X', 'O', 'X'],
['O', 'X', 'O'],
['X', '', 'O'],
];
// En caso de usar React
type State = [string, (newName: string) => void];
const [hero, setHero]: State = useState('thor');
// En caso de códigos RGB
type RGB = [number, number, number];
const rgb: RGB = [255, 0, 106];
// Un problema que existe con las **tuplas**:
rgb.push(4); // Se lo come com Patatas Fritas!!!
// La forma de arreglarlo
type RGB = readonly [number, number, number];
Utilizando emums
// Automáticamente lo maneja como índices desde el 0 al 2 una vez compilado
enum ERROR_TYPES {
NOT_FOUND,
UNAUTHORIZED,
FORBIDDEN,
}
function mostrarMensaje(tipoError: ERROR_TYPES) {
if (tipoError === ERROR_TYPES.NOT_FOUND) {
console.log('No se encuentra el recurso');
} else if (tipoError === ERROR_TYPES.UNAUTHORIZED) {
console.log('Debe iniciar sesión primero');
} else if (tipoError === ERROR_TYPES.FORBIDDEN) {
console.log('No tienes permisos para acceder');
}
}
🗒️ Ésto genera código demás compilado a
JavaScript
// En caso que quiera tener exactamente esos valores
enum ERROR_TYPES {
NOT_FOUND = 'notFound',
UNAUTHORIZED = 'unauthorized',
FORBIDDEN = 'forbidden',
}
function mostrarMensaje(tipoError: ERROR_TYPES) {
if (tipoError === ERROR_TYPES.NOT_FOUND) {
console.log('No se encuentra el recurso');
} else if (tipoError === ERROR_TYPES.UNAUTHORIZED) {
console.log('Debe iniciar sesión primero');
} else if (tipoError === ERROR_TYPES.FORBIDDEN) {
console.log('No tienes permisos para acceder');
}
}
Su Compilado sería esto:
'use strict';
var ERROR_TYPES;
(function (ERROR_TYPES) {
ERROR_TYPES['NOT_FOUND'] = 'notFound';
ERROR_TYPES['UNAUTHORIZED'] = 'unauthorized';
ERROR_TYPES['FORBIDDEN'] = 'forbidden';
})(ERROR_TYPES || (ERROR_TYPES = {}));
function mostrarMensaje(tipoError) {
if (tipoError === ERROR_TYPES.NOT_FOUND) {
console.log('No se encuentra el recurso');
} else if (tipoError === ERROR_TYPES.UNAUTHORIZED) {
console.log('Debe iniciar sesión primero');
} else if (tipoError === ERROR_TYPES.FORBIDDEN) {
console.log('No tienes permisos para acceder');
}
}
Ahora si utilizamos const:
const enum ERROR_TYPES {
NOT_FOUND = 'notFound',
UNAUTHORIZED = 'unauthorized',
FORBIDDEN = 'forbidden',
}
function mostrarMensaje(tipoError: ERROR_TYPES) {
if (tipoError === ERROR_TYPES.NOT_FOUND) {
console.log('No se encuentra el recurso');
} else if (tipoError === ERROR_TYPES.UNAUTHORIZED) {
console.log('Debe iniciar sesión primero');
} else if (tipoError === ERROR_TYPES.FORBIDDEN) {
console.log('No tienes permisos para acceder');
}
}
Generaría:
'use strict';
function mostrarMensaje(tipoError) {
if (tipoError === 'notFound' /* ERROR_TYPES.NOT_FOUND */) {
console.log('No se encuentra el recurso');
} else if (tipoError === 'unauthorized' /* ERROR_TYPES.UNAUTHORIZED */) {
console.log('Debe iniciar sesión primero');
} else if (tipoError === 'forbidden' /* ERROR_TYPES.FORBIDDEN */) {
console.log('No tienes permisos para acceder');
}
}
❗ Utilizaremos
const enum ...siempre que sea posible.
❗ Utilizaremos soloenum ...cuando la funcionalidad se consume desde afuera o estemos realizando una librería.
Utilizaremos Aserciones de tipos.
Imaginemos que estamos trabajando con canvas y typescript.
const canvas = document.getElementById('canvas');
// ¿Cómo sabe TypeSCript que realmente estas recuperando un elemento <canvas />?
if (canvas !== null) {
const ctx = canvas.getContext('2d'); // Se queja por `getContext` no existe en HTMLElement
// Pero necesitamos un tipo más específico: HTMLCanvasElement
}
Para decirle que este elemento lo trate como HTMLCanvasElement:
// Básicamente le estamos diciendo a `TypeScript` que se fie de nosotros
const canvas = document.getElementById('canvas') as HTMLCanvasElement;
if (canvas !== null) {
const ctx = canvas.getContext('2d');
}
Lo mejor que podríamos hacer, es que la aserción lo hagamos despues de la comprobación.
// Aún así si mandamos otro elemento que no sea canvas TypeScript no se daría cuenta.
const canvas = document.getElementById('canvas');
if (canvas !== null) {
const ctx = (canvas as HTMLCanvasElement).getContext('2d');
}
Lo que se puede hacer es comprobar si ese elemento es igual que un HTMLCanvasElement.
// Esto es JavaScript pero en TypeScript usa la inferencia para darse cuenta.
const canvas = document.querySelector('canvas');
if (canvas instanceof HTMLCanvasElement) {
const ctx = canvas.getContext('2d');
}
Vamos a codi.link y probamos.
Fetching de datos en TypeScript.
// Usar .mts para nodejs
const API_URL = 'https://api.github.com/search/repositories?q=javasript';
const response = await fecth(API_URL);
if (!response.ok) {
throw new Error('Request failed');
}
// Aquí TypeScript no tiene ni idea que tipo de datos sería
// Para esto se utiliza las aserciones
const data = await response.json();
Ingresamos a https://quicktype.io/ pegamos la respuesta de la API y obtenemos los tipos checkamos: Interfaces only, Verify JSON.parse results at runtime, Use types instead of interfaces.
export type GitHubAPIResponse = {
items: []
...
}
// ...
const API_URL = 'https://api.github.com/search/repositories?q=javasript'
const response = await fecth(API_URL)
if (!response.ok) {
throw new Error('Request failed')
}
const data = await response.json() as GitHubAPIResponse
data.items.// aparece todos los tipos.
Para realizar validaciones y detectar los tipos en tiempo de ejecución podríamos habilitar TypeScript Zod, esto sí genera código en tiempo de ejecución.
Hay otra forma de definir tipos y es con interface, es muy similar al type, diría que mas del 95% podrían ser intercambiables.
type heroe = {
id: string;
name: string;
age: number;
};
interface heroe {
id: string;
name: string;
age: number;
}
Tiene ligerias diferencias, define la forma que debe tener un objeto, sería cómo definir el contrato de un objeto para especificar sus propiedades y métodos.
Se puede añadir que tenga métodos:
interface heroe {
id: string;
name: string;
age: number;
saludar: () => void;
}
Lo interesante es que en las interfaces se pueden anidar.
interface Producto {
id: number;
nombre: string;
precio: number;
quantity: number;
}
// Algo propio de las `interface` se puede extender con `extends`
interface Zapatilla extends Producto {
talla: number;
}
interface CarritoDeCompras {
totalPrice: number;
productos: (Producto | Zapatilla)[]; // Se puede utilizar union types con las interfaces
}
const carrito: CarritoDeCompras = {
totalPrice: 100,
productos: [
{
id: 1,
nombre: 'Producto 1',
precio: 100,
quantity: 1,
},
],
};
Hay dos formas de tipar métodos con interfaces.
// Opción 1
interface CarritoOps {
add: (product: Producto) => void;
remove: (id: number) => void;
clear: () => void;
}
// Opción 2
interface CarritoOps {
add(product: Product): void;
remove(id: number): void;
clear(): void;
}
Algo que podemos hacer con las interfaces es reescribir la interfaz y dividir los tipos internos, si lo dejamos así se queja por tener las propiedades repetidas.
interface CarritoOps {
add: (product: Producto) => void;
remove: (id: number) => void;
}
interface CarritoOps {
clear(): void;
}
const ops: CarritoOps = {
add: (product: Producto) => {},
remove: (id: number) => {},
clear: () => {},
};
¿Interfaces o Types?
Podemos ver las diferencias de utilizar uno o el otro desde la documentación y apartir de esto escoger cual utilizar.
Doc. TypeScript - Handbook - EverydayTypes
Otra diferencia es que las interfaces no pueden declarar tipo primitivo, ej:
type HeroId = `${number}-${string}`;
Por lo general siempre que puedas usar type utilizalos, para objetos o clases utilizaría interface.
Narrowing
function mostrarLongitud(objeto: number | string) {
if (typeof objeto === 'string') {
return objeto.length;
}
return objeto.toString().length;
}
mostrarLongitud('1');
interface Mario {
company: 'Nintendo';
nombre: string;
saltar: () => void;
}
interface Sonic {
company: 'Sega';
nombre: string;
correr: () => void;
}
type Personaje = Mario | Sonic;
function jugar(personaje: Personaje) {
if (personaje.company === 'Nintendo') {
personaje.saltar(); // <--- Este es Mario!!
return;
}
// Seguro que solo llega a sonic
personaje.correr();
}
interface Mario {
nombre: string;
saltar: () => void;
}
interface Sonic {
nombre: string;
correr: () => void;
}
type Personaje = Mario | Sonic;
// Type Guard
// Déjame comprobar si personaje es sonic
// y esta función determina si es sonic o no
function checkIsSonic(personaje: Personaje): personaje is Sonic {
return (personaje as Sonic).correr === undefined;
}
function jugar(personaje: Personaje) {
if (checkIsSonic(personaje)) {
personaje.correr();
}
}
Tipo Never
function fn(x: string | number) {
if (typeof x === 'string') {
// x es string
x.toUpperCase();
} else if (typeof x === 'number') {
// do something
x.toFixed(2);
} else {
x; // never
}
}
class Avenger {
readonly name: string;
// #powerScore: number en todos tendríamos que añadir #
private powerScore: number; // no puedes acceder a partir de la instancia
private readonly wonBattles: number = 0;
// public age: number = 0
// age: number = 0 // por defecto es public
protected age: number = 0; // podrías acceder a clases que hereden esta propiedad
constructor(name: string, powerScore: number) {
this.name = name;
this.powerScore = powerScore;
}
get fullName() {
return `${this.name}, de poder ${this.powerScore}`;
}
set power(newPower: number) {
if (newPower <= 100) {
this.powerScore = newPower;
} else {
throw new Error('Power score cannot be more than 100');
}
}
}
const avengers = new Avenger('Spidey', 80);
// avengers.name
interface Avenger {
name: string;
powerScore: number;
wonBattles: number;
age: number;
battle: (enemy: Avenger, win: boolean) => void;
}
class Avenger implements Avenger {
constructor(name: string, powerScore: number) {
this.name = name;
this.powerScore = powerScore;
}
get fullName() {
return `${this.name}, de poder ${this.powerScore}`;
}
set power(newPower: number) {
if (newPower <= 100) {
this.powerScore = newPower;
} else {
throw new Error('Power score cannot be more than 100');
}
}
}
const avengers = new Avenger('Spidey', 80);