Saltar al contenido
chuleta.ts · devschool

Chuleta de TypeScript

Curso de TypeScript

Resumen de consulta rápida con lo esencial de TypeScript 5 en modo strict. Cada apartado enlaza a la lección donde se explica con calma.

Instalar y compilar

TerminalQué hace
npm init -yCrea package.json
npm install --save-dev typescriptInstala el compilador en el proyecto
npx tsc --initCrea tsconfig.json
npx tscCompila el proyecto según tsconfig.json
npx tsc app.tsCompila un archivo suelto a app.js
npx tsc --watchRecompila al guardar
npx tsc --noEmitSolo comprueba tipos, sin generar JS
node app.jsEjecuta el JavaScript generado
node app.tsNode.js reciente ejecuta .ts directamente (sin comprobar tipos)
npx tsx app.tsEjecuta .ts con tsx

Consejo: los tipos desaparecen al compilar. Solo sirven para que el compilador te avise antes de ejecutar.

Más en: ¿Qué es TypeScript? · Instalar y compilar

Tipos básicos

CódigoQué hace
let nombre: string = "Ana";Texto
let edad: number = 30;Número (enteros y decimales)
let activo: boolean = true;Booleano
const IVA = 0.21;Con const el tipo es el valor: 0.21
let x = 5;Inferencia: TypeScript deduce number
let id: bigint = 10n;Enteros muy grandes
let a: null = null;Solo admite null
let u: undefined = undefined;Solo admite undefined
let dato: unknown;Cualquier valor, pero hay que comprobarlo antes de usarlo
let cualquiera: any;Desactiva la comprobación: evítalo
function fallar(): never { throw new Error("x"); }Nunca devuelve nada
function log(): void {}No devuelve nada útil
valor as stringAserción de tipo: tú aseguras el tipo
valor!Aserción non-null: afirma que no es null
valor satisfies TipoComprueba el tipo sin ensancharlo

Más en: Tipos básicos

Arrays y tuplas

CódigoQué hace
let nums: number[] = [1, 2, 3];Array de números
let nombres: Array<string> = [];Misma idea con sintaxis genérica
let matriz: number[][] = [[1], [2]];Array de arrays
const fijo: readonly number[] = [1, 2];Array de solo lectura
let par: [string, number] = ["Ana", 30];Tupla: longitud y tipos fijos
let pto: [x: number, y: number];Tupla con nombres
let opc: [string, number?];Elemento opcional
const [nombre, edad] = par;Desestructura la tupla
nums[5]Tipo number (con noUncheckedIndexedAccess: number | undefined)
nums.map(n => n * 2)Se infiere number[]
const colores = ["rojo", "azul"] as const;Tupla de solo lectura con literales
typeof colores[number]"rojo" | "azul"

Más en: Arrays y tuplas

Interfaces y type

interface Usuario {
  readonly id: number;
  nombre: string;
  email?: string;
}

type Punto = { x: number; y: number };
CódigoQué hace
interface A { x: number }Describe la forma de un objeto
type A = { x: number }Alias: también describe objetos
email?: stringPropiedad opcional
readonly id: numberNo se puede reasignar
interface B extends A { y: number }Extiende una interfaz
type C = A & { y: number }Intersección: une tipos
[clave: string]: numberFirma de índice: claves libres
Record<string, number>Objeto con claves string y valores number
type Id = string | number;Alias de una unión
type Callback = (x: number) => void;Alias de una función
interface A { x: number } repetidaSe fusionan (solo con interface)
typeof objetoTipo de un valor existente
keyof Usuario"id" | "nombre" | "email"
Usuario["nombre"]string (tipo de una propiedad)

Consejo: usa interface para objetos y type para uniones, tuplas y funciones.

Más en: Objetos e interfaces · Uniones y alias

Uniones y literales

CódigoQué hace
let id: string | number;Acepta uno de los dos tipos
let rol: "admin" | "user";Tipo literal: solo esos valores
type Estado = "cargando" | "ok" | "error";Alias de literales
let n: number | null = null;Valor opcional explícito
let a: 1 | 2 | 3;Literales numéricos
type Resp = { ok: true; datos: string } | { ok: false; error: string };Unión discriminada
const r = "ok";Tipo "ok" (con let sería string)
as constCongela un objeto con literales de solo lectura
type Ruta = `/${string}`;Tipo plantilla: empieza por /
type Resp =
  | { ok: true; datos: string }
  | { ok: false; error: string };

function mostrar(r: Resp): string {
  return r.ok ? r.datos : r.error;
}

Más en: Uniones y alias

Narrowing

CódigoQué hace
typeof x === "string"Dentro del if, x es string
x instanceof DateDentro del if, x es Date
"nombre" in objComprueba si existe la propiedad
Array.isArray(x)Dentro del if, x es un array
if (x)Descarta null, undefined, 0 y ""
x !== nullDescarta null
x ?? "defecto"Valor por defecto si es null o undefined
x?.propiedadAcceso seguro: undefined si x es nulo
r.ok === trueEstrecha una unión discriminada
function esTexto(x: unknown): x is stringGuarda de tipo propia
function afirmar(x: unknown): asserts x is stringFunción que lanza si no se cumple
const _: never = valor;Comprueba que cubriste todos los casos
function longitud(x: string | string[]): number {
  if (typeof x === "string") {
    return x.length;
  }
  return x.length; // aquí x es string[]
}

Más en: Narrowing

Funciones

function precioFinal(precio: number, iva: number = 0.21): number {
  return Math.round(precio * (1 + iva) * 100) / 100;
}

const saludar = (nombre: string): string => `Hola, ${nombre}`;
CódigoQué hace
(a: number, b: number): numberTipos de parámetros y de retorno
(x?: number)Parámetro opcional (number | undefined)
(x = 10)Valor por defecto (se infiere number)
(...nums: number[])Parámetros rest
(): voidNo devuelve nada
type Op = (a: number, b: number) => number;Tipo de una función
(cb: (x: number) => void)Parámetro que es una función
(): Promise<string>Función asíncrona
(this: Elemento)Declara el tipo de this
function f(x: string): string;Sobrecarga: firma antes de la implementación
(obj: { id: number; nombre: string })Parámetro con forma de objeto
([a, b]: [number, number])Desestructura una tupla
ReturnType<typeof f>Tipo que devuelve f
Parameters<typeof f>Tupla con los parámetros de f

Más en: Funciones

Clases

class Cuenta {
  static total = 0;

  constructor(
    public readonly titular: string,
    private saldo: number = 0,
  ) {
    Cuenta.total++;
  }

  ingresar(cantidad: number): void {
    this.saldo += cantidad;
  }

  get balance(): number {
    return this.saldo;
  }
}
CódigoQué hace
public / private / protectedVisibilidad (solo en compilación)
#saldo = 0;Privado de verdad también en JavaScript
readonly id: numberSolo se asigna en el constructor
constructor(private x: number) {}Declara y asigna la propiedad
class B extends A { ... }Herencia
super(x)Llama al constructor del padre
override metodo() {}Marca que sobrescribe uno del padre
class A implements Interfaz { ... }La clase cumple un contrato
abstract class Figura { abstract area(): number; }Clase que no se instancia
static contar() {}Método de la clase
get x() / set x(v)Getter y setter
class Caja<T> { ... }Clase genérica
x: string; sin valorError en strict: inicialízala en el constructor
x!: string;Afirma que se asignará más tarde

Más en: Clases

Enums

CódigoQué hace
enum Dir { Arriba, Abajo }Enum numérico: Dir.Arriba vale 0
enum Dir { Arriba = 1, Abajo }Empieza en 1: Abajo vale 2
enum Color { Rojo = "ROJO" }Enum de texto
const enum Dir { ... }Se sustituye por el valor al compilar
Dir[0]"Arriba" (solo enums numéricos)
let d: Dir = Dir.Abajo;Variable del tipo del enum
keyof typeof ColorUnión de los nombres: "Rojo"
const Rol = { Admin: "admin", User: "user" } as const;Alternativa sin enum
type Rol = typeof Rol[keyof typeof Rol];"admin" | "user"

Consejo: muchas veces basta una unión de literales: type Rol = "admin" \| "user". Es más simple y no genera código.

Más en: Enums

Genéricos

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

const n = primero([1, 2, 3]);      // number | undefined
const s = primero(["a", "b"]);     // string | undefined
CódigoQué hace
function f<T>(x: T): TFunción genérica
f<string>("a")Indica el tipo a mano
interface Caja<T> { valor: T }Interfaz genérica
type Par<A, B> = [A, B];Alias con varios parámetros
<T extends { id: number }>Restricción: T debe tener id
<T, K extends keyof T>(obj: T, k: K): T[K]Lee una propiedad con su tipo exacto
<T = string>Tipo por defecto
Promise<T>Promesa que resuelve a T
Array<T> / Map<K, V> / Set<T>Tipos genéricos integrados
async function cargar<T>(url: string): Promise<T>Función genérica asíncrona

Más en: Genéricos

Tipos utilitarios

Código (con Usuario { id, nombre, email })Resultado
Partial<Usuario>Todas las propiedades opcionales
Required<Usuario>Todas obligatorias
Readonly<Usuario>Todas de solo lectura
Pick<Usuario, "id" | "nombre">Solo esas propiedades
Omit<Usuario, "email">Todas menos email
Record<"a" | "b", number>{ a: number; b: number }
Exclude<"a" | "b" | "c", "a">"b" | "c"
Extract<"a" | "b", "a" | "z">"a"
NonNullable<string | null>string
ReturnType<typeof f>Tipo que devuelve f
Parameters<typeof f>Tupla con los parámetros de f
Awaited<Promise<string>>string
keyof Usuario"id" | "nombre" | "email"
{ [K in keyof T]: boolean }Tipo mapeado: recorre las claves

Más en: Tipos utilitarios

Módulos

CódigoQué hace
export function sumar() {}Exporta desde un módulo
export default class Carrito {}Exportación por defecto
export type { Usuario };Exporta solo un tipo
import { sumar } from "./utils.js";Importa con nombre
import type { Usuario } from "./tipos.js";Importa solo el tipo (se borra al compilar)
import * as util from "./utils.js";Importa todo en un objeto
declare module "libreria";Declara tipos de un módulo sin ellos
npm install --save-dev @types/nodeInstala tipos de una librería
.d.tsArchivo solo de declaraciones de tipos

Cuidado: en módulos de Node.js escribe la extensión .js en el import, aunque el archivo sea .ts.

Más en: Módulos y tsconfig

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "strict": true,
    "outDir": "dist",
    "rootDir": "src",
    "noUncheckedIndexedAccess": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}
Opción de tsconfig.jsonQué hace
"strict": trueActiva todas las comprobaciones estrictas
"target"Versión de JavaScript que genera
"module"Sistema de módulos de salida
"outDir" / "rootDir"Carpeta de salida / de origen
"noUncheckedIndexedAccess"Los índices de array pueden dar undefined
"noImplicitAny"Prohíbe any implícito (incluido en strict)
"strictNullChecks"null y undefined no valen para todo (incluido en strict)
"lib": ["ES2022", "DOM"]Tipos disponibles (DOM para navegador)
"include" / "exclude"Qué archivos entran en el proyecto

Más en: Módulos y tsconfig · Instalar y compilar

DOM

CódigoQué hace
document.querySelector("h1")HTMLHeadingElement | null
document.querySelector<HTMLInputElement>("#email")HTMLInputElement | null
document.getElementById("menu")HTMLElement | null
if (el) { ... }Comprueba que no sea null antes de usarlo
el?.classList.add("activo")Acceso seguro
document.querySelector("#x") as HTMLButtonElementAserción: tú aseguras que existe
input.valuestring (solo en inputs)
input.valueAsNumbernumber
boton.addEventListener("click", (e: MouseEvent) => { ... })Evento con tipo
form.addEventListener("submit", (e: SubmitEvent) => ...)Envío de formulario
e.target as HTMLInputElementElemento que originó el evento
e.currentTargetElemento al que está pegado el listener
new FormData(form)Recoge los campos
document.createElement("li")HTMLLIElement
"lib": ["DOM"] en tsconfig.jsonNecesario para tener los tipos del DOM
const input = document.querySelector<HTMLInputElement>("#nombre");
const boton = document.querySelector<HTMLButtonElement>("#enviar");

boton?.addEventListener("click", () => {
  if (input) {
    console.log(input.value.trim());
  }
});

Más en: DOM con TypeScript · Proyecto

TypeScript · devschool.es/chuletas/typescript/