Cheatsheet TypeScript
JavaScript com tipagem estática
TypeScript
Tipos Básicos
Tipos Primitivos
let nombre: string = "Ana"; let edad: number = 30; let activo: boolean = true; let nada: null = null; let indef: undefined = undefined;
Los tipos primitivos son string, number y boolean, más null y undefined. La anotación : tipo es opcional cuando el valor permite inferencia.
Union Types
let id: string | number;
id = "abc";
id = 123;
function format(v: string | number): string {
return String(v);
}Un union type permite que un valor sea de varios tipos, separados por |. Dentro de la función solo puedes usar operaciones comunes a todos; para operaciones específicas haz narrowing.
const assertion (as const)
const config = {
host: "localhost",
puerto: 3000,
} as const;
// config.puerto = 4000; // ERROR: readonly
type Puertas = typeof config.puerto; // 3000as const hace el valor profundamente inmutable y convierte los tipos en literales (3000 en vez de number). Es útil para constantes y para crear tipos a partir de datos.
Arrays y Tuplas
let nums: number[] = [1, 2, 3]; let textos: Array<string> = ["a", "b"]; let tuplo: [string, number] = ["Ana", 30]; tuplo[0]; // string
Los arrays se tipan con tipo[] o Array<tipo>. Una tupla es un array con tamaño y tipos fijos por posición, útil para pares de valores.
Literal Types
let dir: "izquierda" | "derecha"; dir = "izquierda"; type Status = "activo" | "inactivo"; let s: Status = "activo";
Un literal type restringe la variable a valores exactos. Combinado con unions crea conjuntos cerrados de opciones, más seguros que string. Ideal para estados y configuraciones.
typeof e instanceof
function area(v: number | Date) {
if (typeof v === "number") {
return v * v; // number
}
return v.getTime(); // Date
}Los operadores typeof (para primitivos) e instanceof (para clases) hacen narrowing: dentro del if TypeScript afina el tipo automáticamente, permitiendo operaciones específicas.
any, unknown, never, void
let x: any = "qualquer"; // sin verificación
let y: unknown = 42; // seguro, exige check
function error(): never {
throw new Error("fallo");
}
function log(): void { } // sin retornoany desactiva la verificación (evítalo). unknown acepta cualquier valor pero obliga a verificar antes de usar. never es para funciones que nunca retornan y void para las que no devuelven valor.
Optional y null
interface User {
nombre: string;
email?: string; // opcional
}
let valor: string | null = null;
let def: string = valor ?? "patrón";El ? marca una propiedad opcional (puede ser undefined). El operador ?? (nullish coalescing) devuelve el valor de la derecha si el de la izquierda es null o undefined.
Objetos Tipados
let ponto: { x: number; y: number } = {
x: 1,
y: 2,
};
let user: { nombre: string; edad?: number };Puedes tipar objetos directamente con { campo: tipo }. Cada propiedad es obligatoria salvo que se marque con ?. Para formas reutilizadas, prefiere una interface o type.
Inferencia de Tipos
let nombre = "Ana"; // string
let nums = [1, 2, 3]; // number[]
const obj = { x: 1, y: 2 };
// nombre = 10; // ERROR: no es numberTypeScript infiere el tipo a partir del valor inicial, por eso muchas anotaciones son innecesarias. Las variables const tienen tipos más específicos. Deja que el compilador infiera siempre que sea posible.
Type Assertion (as)
let el = document.getElementById("app") as HTMLDivElement;
let n = ("123" as string).length;
let input = document.querySelector("input")!;Una type assertion con as le dice al compilador que trate un valor como un tipo específico, sin verificación. Úsala solo cuando sabes más que TypeScript. No convierte el valor en runtime.
bigint y symbol
let grande: bigint = 9007199254740991n;
let id: symbol = Symbol("id");
let clave: unique symbol = Symbol("clave");bigint representa enteros arbitrariamente grandes (sufijo n) y symbol crea identificadores únicos. unique symbol es un tipo único asociado a una constante.
Funções
Función Tipada
function sumar(a: number, b: number): number {
return a + b;
}
const doble = (x: number): number => x * 2;Tipa los parámetros y el retorno de una función. El retorno a menudo se infiere, pero anotarlo documenta el contrato. Las funciones arrow siguen la misma regla.
Tipo de Función
type Callback = (data: string) => void;
function fetchDados(cb: Callback) {
cb("resultado");
}
fetchDados((d) => console.log(d));Puedes definir un tipo de función con type, describiendo parámetros y retorno. Es reutilizable en varias firmas y hace los callbacks más legibles y seguros.
Retorno void vs undefined
function log(msg: string): void {
console.log(msg);
}
function achar(id: number): string | undefined {
return undefined;
}Un retorno void significa que la función no devuelve nada útil. En cambio, undefined como tipo de retorno indica que puede devolver ese valor explícitamente. Son conceptos distintos.
Parámetros Opcionales y Default
function saudar(nombre: string, título?: string) {
return título ? `${título} ${nombre}` : nombre;
}
function conectar(host: string = "localhost") { }Un parámetro opcional se marca con ? y puede ser undefined. Un parámetro con valor por defecto usa = valor y ya es opcional en la llamada. Los opcionales deben ir después de los obligatorios.
Arrow Function Tipada
const multiplicar: (a: number, b: number) => number =
(a, b) => a * b;
const ítems = [1, 2, 3].map((n: number) => n * 2);En arrow functions, el tipo se declara en la variable con la sintaxis (params) => retorno. Los parámetros de la implementación se infieren. En .map() el tipo del item se infiere del array.
Función Genérica Simple
function identidad<T>(valor: T): T {
return valor;
}
identidad<string>("hola");
identidad(42); // T inferido como numberUna función genérica usa <T> para aceptar cualquier tipo manteniendo la relación entre entrada y salida. El tipo puede ser explícito o inferido. Es la base de la programación reutilizable.
Rest Parameters
function suma(...nums: number[]): number {
return nums.reduce((a, b) => a + b, 0);
}
suma(1, 2, 3, 4); // 10El rest parameter ...nombre: tipo[] acepta un numero variable de argumentos, tratados como un array tipado. Debe ser el último parámetro de la función.
Callbacks Tipados
type Handler = (evento: string, código: number) => void;
function registrar(h: Handler) {
h("click", 200);
}
registrar((ev, cod) => console.log(ev, cod));Al pasar callbacks, típalos para que los parámetros se infieran en el uso. Así el compilador verifica los argumentos y el retorno, evitando errores en funciones de orden superior.
Function Overloads
function parse(v: string): number;
function parse(v: number): string;
function parse(v: string | number) {
return typeof v === "string"
? parseInt(v) : String(v);
}Los overloads definen varias firmas para la misma función. Las primeras líneas declaran los casos y la última es la implementación. El compilador elige la firma correcta según el argumento.
this Tipado
interface Elemento {
nombre: string;
mostrar(this: Elemento): void;
}
function mostrar(this: Elemento) {
console.log(this.nombre);
}TypeScript permite tipar this como un pseudo-parámetro en la primera posición. No cuenta como argumento real, pero ayuda al compilador a verificar el contexto de llamada.
Interfaces e Types
Interface Básica
interface Persona {
nombre: string;
edad: number;
email?: string; // opcional
}
const p: Persona = { nombre: "Ana", edad: 30 };Una interface define la forma de un objeto: nombres y tipos de las propiedades. El ? marca campos opcionales. Es la forma más común de tipar objetos en TypeScript.
Index Signature
interface Dict {
[key: string]: number;
}
const notas: Dict = { math: 95, port: 88 };
type Mapa = Record<string, number>;Una index signature [key: string]: tipo describe objetos con claves dinámicas. El utilitario Record<string, tipo> es la forma moderna y concisa de hacer lo mismo.
Declaration Merging
interface Ventana {
título: string;
}
interface Ventana {
ancho: number;
}
// Ventana tem título E anchoEl declaration merging junta automáticamente interfaces con el mismo nombre en una sola. Es útil para extender tipos de bibliotecas. Solo funciona con interface, no con type.
type alias
type Ponto = {
x: number;
y: number;
};
type ID = string | number;type crea un alias para cualquier tipo, incluyendo objetos, unions y primitivos. A diferencia de la interface, acepta unions y tipos compuestos. Para objetos simples son casi equivalentes.
Readonly
interface Config {
readonly host: string;
readonly puerto: number;
}
const cfg: Config = { host: "x", puerto: 80 };
// cfg.host = "y"; // ERROREl modificador readonly hace una propiedad inmutable tras la creación. Intentar reasignar causa un error de compilación. Para arrays usa readonly tipo[] o ReadonlyArray.
Extends Múltiple
interface TemId { id: number; }
interface TemData { criadoEm: Date; }
interface Entidad extends TemId, TemData {
nombre: string;
}Una interface puede extender varias interfaces a la vez, separadas por comas. La interface resultante hereda todos los campos. Es una forma limpia de componer contratos reutilizables.
interface vs type
// interface: extensible, merging
interface A { x: number; }
interface A { y: number; } // merge
// type: unions, mapeados
type B = string | number;Usa interface para contratos de objetos públicos (permite declaration merging y extends) y type para unions, tuplas y tipos derivados. Ambos funcionan para objetos.
Interface para Función
interface Comparador {
(a: number, b: number): number;
}
const cmp: Comparador = (a, b) => a - b;Una interface puede describir una firma de función con (params): retorno. Los parámetros de la implementación se infieren. Es una alternativa al type de función.
Type Derivado de Objeto
const config = {
host: "localhost",
puerto: 3000,
} as const;
type Config = typeof config;El operador typeof en posición de tipo crea un tipo a partir de un valor existente. Combinado con as const, genera tipos literales precisos. Evita duplicar la definición.
Extends
interface Animal {
nombre: string;
}
interface Perro extends Animal {
raca: string;
}
const c: Perro = { nombre: "Rex", raca: "Labrador" };extends crea una interface que hereda los campos de otra y añade los suyos. La interface hija incluye todas las propiedades del padre. Es la forma de componer contratos.
Intersection Types (&)
type Nombre = { nombre: string };
type Edad = { edad: number };
type Persona = Nombre & Edad;
const p: Persona = { nombre: "Ana", edad: 30 };Un intersection type combina varios tipos con &, exigiendo las propiedades de todos. Es lo opuesto al union: en vez de "uno u otro", es "todos en conjunto".
Classes
Clase con Tipos
class Persona {
nombre: string;
private edad: number;
constructor(nombre: string, edad: number) {
this.nombre = nombre;
this.edad = edad;
}
}En una class, declara los campos con sus tipos antes del constructor. TypeScript verifica las asignaciones en this. Todos los campos obligatorios deben ser inicializados.
Implements
interface Serializable {
toJSON(): string;
}
class User implements Serializable {
toJSON() { return "{}"; }
}implements garantiza que una clase cumple el contrato de una interface. Si falta un método o propiedad, el compilador avisa. Una clase puede implementar varias interfaces.
override
class Base {
método() { return "base"; }
}
class Filha extends Base {
override método() { return "filha"; }
}La palabra clave override marca explícitamente que un método sustituye al del padre. Con la opción noImplicitOverride en el tsconfig, es obligatoria y evita errores de renombrado.
Modificadores de Acceso
class Cuenta {
public titular: string;
private saldo: number = 0;
protected id: number = 0;
readonly banco: string = "CGD";
}public es visible en todas partes, private solo en la clase, protected en la clase y subclases y readonly impide la reasignación. Por defecto los miembros son public.
Abstract Class
abstract class Forma {
abstract area(): number;
descripcion() { return "forma"; }
}
// new Forma(); // ERRORUna clase abstract no puede ser instanciada directamente y sirve de base. Puede tener métodos abstractos (sin cuerpo, con abstract) que las subclases están obligadas a implementar.
Clase Genérica
class Pilha<T> {
private ítems: T[] = [];
push(item: T) { this.ítems.push(item); }
pop(): T | undefined { return this.ítems.pop(); }
}
const p = new Pilha<number>();Una clase genérica usa <T> tras el nombre para parametrizar los tipos de sus miembros. El tipo se define al instanciar con new Clase<tipo>() o se infiere.
Parameter Properties
class Ponto {
constructor(
public x: number,
public y: number
) {}
}
const p = new Ponto(1, 2);Un parameter property declara y asigna un campo directamente en el constructor, juntando un modificador (public, private, readonly) al parámetro. Reduce mucho el código boilerplate.
Getters y Setters
class Círculo {
constructor(private radio: number) {}
get area(): number {
return Math.PI * this.radio ** 2;
}
set nuevoRadio(r: number) { this.radio = r; }
}Los getters (get) y setters (set) definen acceso controlado a una propiedad. Se acceden como campos normales, pero permiten validación o cálculo por detrás.
Herencia (extends)
class Animal {
constructor(public nombre: string) {}
hablar() { return "..."; }
}
class Perro extends Animal {
hablar() { return "Au!"; }
}La herencia con extends permite que una clase hija reutilice y sustituya métodos del padre. El constructor de la hija debe llamar a super() si el padre tiene uno.
Static
class MathUtils {
static readonly PI = 3.14159;
static doble(n: number): number {
return n * 2;
}
}
MathUtils.doble(5);Los miembros static pertenecen a la clase y no a la instancia, y se acceden con Clase.miembro. Pueden ser métodos, campos o readonly. Útiles para utilitarios y constantes.
Generics
Función Genérica
function primero<T>(arr: T[]): T | undefined {
return arr[0];
}
primero([1, 2, 3]); // number
primero(["a", "b"]); // stringUna función genérica declara <T> antes de los parámetros. El tipo se infiere a partir de los argumentos y mantiene la relación entre entrada y salida, sin recurrir a any.
Genéricos con Default
interface Respuesta<T = unknown> {
datos: T;
error?: string;
}
const r: Respuesta<string[]> = { datos: [] };
const s: Respuesta = { datos: null };Un parámetro genérico puede tener un valor por defecto con T = tipo. Si no se indica, usa el default. Útil para hacer tipos opcionales sin perder seguridad.
Constraint con keyof
function ordenar<T, K extends keyof T>(
ítems: T[], clave: K
): T[] {
return [...ítems].sort(
(a, b) => (a[clave] > b[clave] ? 1 : -1)
);
}Combinar K extends keyof T con arrays permite funciones seguras que operan sobre una propiedad específica. El compilador verifica que la clave existe en T.
Interface Genérica
interface Caja<T> {
valor: T;
abrir(): T;
}
const c: Caja<number> = {
valor: 42,
abrir: () => 42,
};Una interface genérica usa <T> para crear tipos reutilizables. El parámetro se suministra al usarla (Caja<number>). Muy común en respuestas de API y contenedores.
Múltiples Parámetros
function juntar<A, B>(a: A, b: B): A & B {
return { ...a, ...b };
}
const r = juntar({ x: 1 }, { y: 2 });
// r: { x: number } & { y: number }Una función puede tener varios parámetros de tipo, separados por comas. Cada uno se infiere independientemente. El retorno A & B combina las formas de los dos objetos.
Genéricos en Promises
async function fetchUser(id: number): Promise<User> {
const r = await fetch(`/api/${id}`);
return r.json();
}
const u = await fetchUser(1); // UserPromise<T> es genérico: indica el tipo del valor resuelto. Al anotar el retorno de funciones async, el await devuelve el tipo correcto. Esencial para código asíncrono type-safe.
Constraints (extends)
function mayor<T extends { length: number }>(
a: T, b: T
): T {
return a.length > b.length ? a : b;
}Una constraint T extends Tipo restringe el genérico a tipos que tengan cierta forma. Aquí garantiza que T tiene length, permitiendo acceder a esa propiedad con seguridad.
Indexed Access (T[K])
interface User {
nombre: string;
edad: number;
}
type Nombre = User["nombre"]; // string
type Valor = User[keyof User]; // string | numberUn indexed access T[K] extrae el tipo de una propiedad de otro tipo. Combinado con keyof, construye unions de valores. Muy útil en utilitarios genéricos.
keyof
function get<T, K extends keyof T>(
obj: T, key: K
): T[K] {
return obj[key];
}
get({ nombre: "Ana" }, "nombre"); // stringkeyof T produce un union de las claves de T. Combinado con K extends keyof T, garantiza que la clave existe. El tipo de retorno T[K] es el tipo de esa propiedad.
Arrow Genérica en .tsx
// en .ts const id = <T>(x: T): T => x; // en .tsx (React) usa trailing comma const id2 = <T,>(x: T): T => x;
En archivos .tsx, la arrow genérica <T> se confunde con JSX. Añade una coma <T,> o usa <T extends unknown> para que el parser reconozca el genérico.
Utility Types
Partial / Required
type Parcial = Partial<Persona>; // todos los campos opcionales type Completo = Required<Parcial>; // todos obligatorios
Partial<T> hace todas las propiedades opcionales, útil para funciones de actualización. Required<T> hace lo inverso, quitando los ? y haciendo todo obligatorio.
ReturnType
function sumar(a: number, b: number) {
return a + b;
}
type R = ReturnType<typeof sumar>; // numberReturnType<T> extrae el tipo de retorno de una función. Se usa con typeof funcion para obtener el tipo de la función. Evita duplicar el tipo de retorno manualmente.
Awaited
type A = Awaited<Promise<string>>; // string type B = Awaited<Promise<number[]>>; // number[] type C = Awaited<boolean>; // boolean
Awaited<T> "desenvuelve" el tipo resuelto de una Promise, tal como el operador await. Funciona recursivamente en promises anidadas. Útil para tipar resultados asíncronos.
Pick / Omit
type Nombre = Pick<Persona, "nombre">; type SemEmail = Omit<Persona, "email">; type Basico = Pick<User, "id" | "nombre">;
Pick<T, K> selecciona solo las propiedades indicadas y Omit<T, K> las excluye, manteniendo el resto. Son ideales para crear variantes de un tipo sin reescribirlo.
Extract / Exclude
type T = "a" | "b" | "c"; type AB = Extract<T, "a" | "b">; // "a" | "b" type C = Exclude<T, "a" | "b">; // "c"
Extract<T, U> mantiene de un union solo los tipos asignables a U; Exclude<T, U> los elimina. Son útiles para filtrar miembros de unions de literales.
InstanceType
class User {
nombre = "Ana";
}
type U = InstanceType<typeof User>; // User
type Ctor = new () => User;InstanceType<T> extrae el tipo de la instancia de un constructor. Se usa con typeof Clase. Es común en fábricas genéricas que reciben constructores y devuelven instancias.
Record
type Notas = Record<string, number>;
const n: Notas = { math: 95, port: 88 };
type Mapa = Record<"a" | "b", boolean>;Record<K, V> crea un objeto cuyas claves son del tipo K y valores del tipo V. Más preciso que una index signature cuando las claves son conocidas.
NonNullable
type T = string | null | undefined;
type NN = NonNullable<T>; // string
function usar(v: string | undefined) {
const s: NonNullable<typeof v> = v ?? "";
}NonNullable<T> elimina null y undefined de un tipo. Es útil para garantizar que un valor está presente tras una verificación, sin reescribir el tipo.
Uppercase / Capitalize
type A = Uppercase<"ana">; // "ANA" type B = Lowercase<"ANA">; // "ana" type C = Capitalize<"ana">; // "Ana" type D = Uncapitalize<"Ana">; // "ana"
Los utility types de template literal transforman strings a nivel de tipo: Uppercase, Lowercase, Capitalize y Uncapitalize. Solo operan en tipos literales de string.
Readonly<T>
type Inmutable = Readonly<Persona>;
const p: Inmutable = { nombre: "Ana", edad: 30 };
// p.edad = 31; // ERROR: readonlyReadonly<T> hace todas las propiedades de T inmutables. Cualquier reasignación causa error. Es superficial (no afecta objetos anidados); para arrays usa ReadonlyArray.
Parameters
function sumar(a: number, b: number) {
return a + b;
}
type P = Parameters<typeof sumar>;
// [a: number, b: number]Parameters<T> extrae los tipos de los parámetros de una función como una tupla. Se usa con typeof. Práctico para reutilizar la firma de funciones existentes.
Tipos Avançados
Type Guards
function procesar(v: string | number) {
if (typeof v === "string") {
return v.toUpperCase();
}
return v.toFixed(2);
}Los type guards usan typeof, instanceof o verificaciones para afinar (narrowing) un union. Dentro de cada rama TypeScript sabe el tipo exacto.
Template Literal Types
type Evento = `on${Capitalize<"click" | "focus">}`;
// "onClick" | "onFocus"
type Rota = `/${string}`;Los template literal types crean tipos string a partir de template literals. Combinados con unions generan todas las combinaciones. Útiles para nombres de eventos, rutas y patrones.
Exhaustiveness (never)
function area(f: Forma): number {
switch (f.tipo) {
case "círculo": return Math.PI * f.radio ** 2;
case "cuadrado": return f.lado ** 2;
default:
const _ex: never = f;
return _ex;
}
}Asignar el caso default a una variable never garantiza exhaustividad: si añades un nuevo caso al union sin tratarlo, el compilador avisa. Hace el switch a prueba de futuro.
Discriminated Unions
type Forma =
| { tipo: "círculo"; radio: number }
| { tipo: "cuadrado"; lado: number };
function area(f: Forma) {
switch (f.tipo) {
case "círculo": return Math.PI * f.radio ** 2;
case "cuadrado": return f.lado ** 2;
}
}Una discriminated union es un union de objetos con un campo común literal (el discriminador, aquí tipo). Un switch sobre él hace narrowing automático y seguro.
infer
type Unpack<T> = T extends Promise<infer U>
? U : T;
type R = Unpack<Promise<string>>; // stringLa palabra clave infer declara una variable de tipo dentro de un conditional, dejando que TypeScript la deduzca. Aquí extrae el tipo interno de una Promise.
Recursive Types
type Json =
| string
| number
| boolean
| null
| Json[]
| { [key: string]: Json };Un recursive type se referencia a sí mismo, describiendo estructuras anidadas como JSON o árboles. TypeScript resuelve la recursión automáticamente. Útil para datos jerárquicos.
Mapped Types
type Opcional<T> = {
[K in keyof T]?: T[K];
};
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};Un mapped type crea un nuevo tipo recorriendo las claves de otro con [K in keyof T]. Permite transformar todas las propiedades programáticamente. Es la base de los utility types.
Narrowing con in
type Perro = { ladrar(): void };
type Gato = { maullar(): void };
function hablar(a: Perro | Gato) {
if ("ladrar" in a) a.ladrar();
else a.maullar();
}El operador in verifica si una propiedad existe en el objeto y hace narrowing del union. Es una alternativa al typeof cuando los tipos se distinguen por campos presentes.
Mapped con Modificadores
type SoLeitura<T> = {
+readonly [K in keyof T]: T[K];
};
type SemOpcionais<T> = {
[K in keyof T]-?: T[K];
};En un mapped type, los prefijos + y - añaden o quitan modificadores. +readonly lo hace inmutable y -? elimina la opcionalidad. Dan control fino sobre la transformación.
Conditional Types
type IsString<T> = T extends string
? "sim" : "no";
type A = IsString<string>; // "sim"
type B = IsString<number>; // "no"Un conditional type elige entre dos tipos con la sintaxis T extends U ? X : Y. Es el "if" del sistema de tipos. Se combina con infer para extraer tipos.
Type Predicates (is)
function isString(v: unknown): v is string {
return typeof v === "string";
}
if (isString(valor)) {
valor.toUpperCase(); // string
}Un type predicate param is Tipo en el retorno crea un guard personalizado. Cuando la función devuelve true, TypeScript afina el tipo en la rama. Ideal para verificaciones reutilizables.
Módulos e Enums
Export / Import
// módulo.ts
export const PI = 3.14;
export function sumar(a: number, b: number) { }
export default class App { }
// uso.ts
import App, { PI, sumar } from "./módulo";export disponibiliza valores y import los trae de otro módulo. export default define la exportación principal, importada sin llaves. Las nombradas usan { }.
Enum Numérico vs String
enum Num { A, B, C } // 0, 1, 2
enum Str { A = "a", B = "b" }
let n: Num = Num.A; // 0
let s: Str = Str.A; // "a"Los enums numéricos se autoincrementan desde 0 y permiten mapeo inverso. Los enums string son más legibles en logs y debugging. Los de string suelen ser más seguros.
Re-export y Barrel
// index.ts (barrel)
export { User } from "./user";
export { Post } from "./post";
export * from "./utils";
import { User, Post } from "./index";Un archivo barrel (generalmente index.ts) reexporta varios módulos con export { } o export *. Simplifica los imports, permitiendo importar todo de un solo lugar.
Type-only Import/Export
import type { User } from "./tipos";
export type { Config } from "./config";
import { type Options, crear } from "./lib";import type importa solo tipos, que se eliminan en la compilación. Evita imports innecesarios en runtime y conflictos. Con verbatimModuleSyntax en el tsconfig se vuelve obligatorio.
Namespace
namespace Utils {
export function format(s: string) {
return s.trim();
}
}
Utils.format("hola");Un namespace agrupa código relacionado bajo un nombre. Es una forma antigua de organización; hoy se prefieren los módulos ES (import/export). Aún útil en código legado o global.
Enums
enum Dirección {
Cima = "UP",
Baixo = "DOWN",
}
let d: Dirección = Dirección.Cima;Un enum define un conjunto de constantes nombradas. Puede tener valores string o numéricos. Se accede con Enum.Miembro. En código moderno, los unions de literales suelen preferirse.
Declaration Files (.d.ts)
// global.d.ts
declare const API_URL: string;
interface Window {
miLib: string;
}Los archivos .d.ts contienen solo declaraciones de tipos, sin código ejecutable. Sirven para tipar bibliotecas sin tipos o variables globales. declare anuncia existencia sin implementar.
Const Enum
const enum Status {
Activo = 1,
Inactivo = 0,
}
let s = Status.Activo; // inlinado como 1Un const enum se inlina completamente en tiempo de compilación, sin generar objeto en runtime. Es más eficiente, pero no puede ser inspeccionado dinámicamente. Úsalo con moderación.
declare module (wildcard)
declare module "*.css" {
const classes: Record<string, string>;
export default classes;
}
declare module "lib-sem-tipos";declare module con un patrón (ej.: "*.css") tipa imports no-TS, como CSS o imágenes. También declara módulos sin tipos para evitar errores. Se coloca en un .d.ts.
Dicas e Configuração
tsconfig.json Esencial
{
"compilerOptions": {
"strict": true,
"target": "ES2022",
"module": "ESNext",
"outDir": "./dist",
"esModuleInterop": true
}
}tsconfig.json configura el compilador. strict: true activa todas las verificaciones rigurosas. target define la versión del JS generado y outDir el destino. Créalo con tsc --init.
strict y Flags Útiles
{
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noImplicitOverride": true
}strict activa strictNullChecks, noImplicitAny y otros. Flags como noUnusedLocals y noImplicitOverride refuerzan la calidad. Activa el máximo que el proyecto permita.
Errores Comunes
// usar == en vez de === // olvidar await en una Promise // any en vez de unknown // mutar un readonly // comparar objetos con ===
Errores frecuentes: usar any en vez de unknown, olvidar await, comparar objetos con === (compara referencia) e ignorar readonly. El strict atrapa la mayoría.
CLI: Comandos
npx tsc --init // crea tsconfig npx tsc --watch // recompila al guardar npx tsc --noEmit // solo verifica tipos npx tsc app.ts // compila un archivo
El compilador tsc compila y verifica tipos. --watch recompila automáticamente y --noEmit solo valida sin generar archivos, útil en CI. Instálalo con npm i -D typescript.
Optional Chaining (?.)
const ciudad = user?.dirección?.ciudad; const método = obj.método?.(); const item = lista?.[0];
El optional chaining ?. accede a propiedades anidadas con seguridad: si algún valor es null o undefined, devuelve undefined en vez de lanzar error. Funciona en métodos e índices.
Buenas Prácticas
// evitar any (usar unknown) // activar strict: true // interfaces para objetos // type para unions/aliases // narrowing en vez de as
Evita any (prefiere unknown), activa strict, usa interface para objetos y type para unions. Prefiere narrowing a as y deja que el compilador infiera siempre que sea posible.
Non-null Assertion (!)
let el = document.getElementById("app")!;
el.textContent = "Hola";
// dice a TS: "no es null/undefined"El operador ! (non-null assertion) asegura al compilador que un valor no es null ni undefined. No hace verificación en runtime; úsalo solo cuando estés seguro.
Definite Assignment (!)
class User {
nombre!: string; // será asignado después
constructor() {
this.init();
}
init() { this.nombre = "Ana"; }
}El ! tras el nombre de un campo (definite assignment) dice a TypeScript que la propiedad será inicializada, aunque no en el constructor. Evita el error de "no inicializado". Úsalo con cuidado.
satisfies (TS 4.9+)
const config = {
host: "localhost",
puerto: 3000,
} satisfies Record<string, string | number>;
config.puerto.toFixed(); // aún inferidoEl operador satisfies valida que un valor cumple un tipo sin perder la inferencia específica. A diferencia de : tipo, mantiene los tipos exactos de las propiedades. Combina seguridad y precisión.
@ts-expect-error
// @ts-expect-error: valor inválido a propósito const n: number = "texto"; // @ts-ignore: suprime el próximo error const x: string = 123;
El comentario @ts-expect-error suprime un error esperado y avisa si deja de existir (mejor que @ts-ignore). Úsalos puntualmente y con justificación, nunca en masa.