TypeScript Quick Reference
Nivel Básico
¿Qué es TypeScript?
TypeScript es JavaScript con tipos.
Añade:
- autocompletado
- detección temprana de errores
- mejor mantenibilidad
- documentación implícita
TypeScript se compila a JavaScript.
Tipos básicos
const name: string = "Alberto";
const age: number = 30;
const isAdmin: boolean = true;
Arrays
const names: string[] = ["Alberto", "Juan"];
Objetos
const user: {
name: string;
age: number;
} = {
name: "Alberto",
age: 30
};
Funciones
function sum(a: number, b: number): number {
return a + b;
}
void
function log(): void {
console.log("hello");
}
La función no devuelve nada útil.
any
let value: any = "hello";
Desactiva TypeScript.
Evitar salvo casos concretos.
unknown
let value: unknown;
Obliga a validar antes de usar.
Más seguro que any.
Union Types
let value: string | number;
Puede ser uno de varios tipos.
Optional Properties
type User = {
name?: string;
}
La propiedad puede no existir.
Type Narrowing
function print(value: string | number) {
if (typeof value === "string") {
value.toUpperCase();
}
}
TypeScript reduce el tipo tras una comprobación.
type vs interface
interface
Más usada para contratos de objetos extensibles.
interface User {
name: string;
}
type
Más flexible para unions y composición.
type Status = "loading" | "success";
Literal Types
const status = "loading";
Tipo inferido: "loading"
as const
Preserva valores literales exactos y añade readonly.
const COLORS = {
primary: "#000"
} as const;
Resultado:
{
readonly primary: "#000";
}
readonly
type User = {
readonly name: string;
}
Evita mutaciones desde TypeScript.
Tuplas
type Pair = [string, number];
Array con posiciones y longitud concretas.
enum
enum Status {
Loading,
Success,
Error
}
Actualmente muchos equipos prefieren as const.
Generics
function identity<T>(value: T): T {
return value;
}
Permiten reutilizar lógica sin perder tipos.
keyof
type User = {
name: string;
age: number;
}
type Keys = keyof User;
Resultado: "name" | "age"
typeof
Obtiene el tipo de una variable.
const user = {
name: "Alberto"
};
type User = typeof user;
Resultado:
type User = {
name: string;
}
Nivel Medio
Utility Types
Partial<T>
Convierte propiedades en opcionales.
Partial<User>
Required<T>
Convierte propiedades opcionales en obligatorias.
Required<User>
Pick<T, K>
Selecciona propiedades concretas.
Pick<User, "name">
Omit<T, K>
Elimina propiedades concretas.
Omit<User, "password">
Record<K, T>
Crea objetos tipo diccionario.
Record<string, number>
Discriminated Unions
type State =
| { status: "loading" }
| { status: "success"; data: string[] }
| { status: "error"; error: string }
Permiten modelar estados válidos.
Muy usado en:
- fetch states
- reducers
- React Query
- state machines
never
Representa un valor imposible.
type A = string & number;
// never
Muy usado en exhaustive checks.
Generic Constraints
function merge<T extends object>(value: T) {}
Restringe qué tipos acepta el genérico.
keyof typeof
const COLORS = {
primary: "#000",
secondary: "#fff"
} as const;
type Color = keyof typeof COLORS;
Resultado: "primary" | "secondary"
Awaited<T>
Desenvuelve Promises.
Awaited<Promise<string>>
// string
ReturnType<T>
Obtiene el tipo de retorno de una función.
ReturnType<typeof getUser>
Awaited<ReturnType<typeof fn>>
Muy usado en loaders y fetch async.
type LoaderData = Awaited<ReturnType<typeof userLoader>>;
Type Assertions
const user = {} as User;
NO valida runtime.
Solo modifica cómo TypeScript interpreta el valor.
Function es mala práctica
onClick: Function
Demasiado genérico. Preferible:
onClick: () => void
{} vs object
{}
Cualquier valor excepto null y undefined.
object
Cualquier valor no primitivo.
useState<User>()
const [user, setUser] = useState<User>()
Realmente es: User | undefined
React.ReactNode
Representa cualquier cosa renderizable por React.
Incluye:
- JSX
- strings
- números
- null
- arrays
JSX.Element
Representa JSX concreto.
<div>Hello</div>
Más restrictivo que ReactNode.
Nivel Avanzado
Mapped Types
Permiten transformar las propiedades de un tipo.
type User = {
name: string;
age: number;
};
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
Resultado:
type ReadonlyUser = {
readonly name: string;
readonly age: number;
}
Base interna de utilities como Readonly<T> o Partial<T>.
Conditional Types
Permiten decidir un tipo según una condición.
type IsString<T> = T extends string
? true
: false;
Ejemplo:
type A = IsString<string>; // true
type B = IsString<number>; // false
infer
Permite extraer un tipo dentro de un conditional type.
type UnwrapPromise<T> =
T extends Promise<infer U>
? U
: T;
Ejemplo:
type Result = UnwrapPromise<Promise<string>>;
// string
Muy usado para crear utilities avanzadas.
Template Literal Types
Permiten construir tipos string a partir de otros strings.
type Size = "sm" | "md" | "lg";
type ClassName = `button-${Size}`;
Resultado:
type ClassName =
| "button-sm"
| "button-md"
| "button-lg";
Muy útil para variantes, eventos o nombres derivados.
Indexed Access Types
Permiten obtener el tipo de una propiedad.
type User = {
id: string;
profile: {
name: string;
age: number;
};
};
type Profile = User["profile"];
También podés obtener tipos de arrays:
type Users = User[];
type SingleUser = Users[number];
// User
Exhaustive Checks
Sirven para asegurarte de que cubrís todos los casos de una union.
type State =
| { status: "loading" }
| { status: "success"; data: string[] }
| { status: "error"; error: string };
function handle(state: State) {
switch (state.status) {
case "loading":
return "Loading";
case "success":
return state.data.join(", ");
case "error":
return state.error;
default:
const exhaustive: never = state;
return exhaustive;
}
}
Si mañana añadís { status: "idle" }, TypeScript marcará error en const exhaustive: never = state porque idle no está gestionado.
satisfies
Comprueba que un valor cumple un tipo sin perder inferencia literal.
type Route = {
path: string;
protected: boolean;
};
const routes = {
home: { path: "/", protected: false },
dashboard: { path: "/dashboard", protected: true }
} satisfies Record<string, Route>;
Con satisfies, routes.home.path sigue siendo "/" (literal). Con anotación directa const routes: Record<string, Route>, perdés esa información concreta.
NonNullable<T>
Elimina null y undefined de un tipo.
type Value = string | null | undefined;
type CleanValue = NonNullable<Value>;
// string
Extract<T, U>
Extrae de una union los tipos compatibles con otro tipo.
type Status = "loading" | "success" | "error";
type FinalStatus = Extract<Status, "success" | "error">;
// "success" | "error"
Exclude<T, U>
Elimina de una union los tipos compatibles con otro tipo.
type Status = "loading" | "success" | "error";
type NonLoadingStatus = Exclude<Status, "loading">;
// "success" | "error"
Derivar tipos desde arrays con as const
Muy usado para evitar duplicar unions.
const ROLES = ["admin", "editor", "viewer"] as const;
type Role = typeof ROLES[number];
// "admin" | "editor" | "viewer"
Composition over Inheritance
TypeScript moderno favorece composición sobre jerarquías grandes de clases.
type WithId = {
id: string;
};
type WithTimestamps = {
createdAt: string;
updatedAt: string;
};
type User = WithId & WithTimestamps & {
name: string;
};
En React encaja mejor con props, hooks y funciones que una jerarquía profunda de clases.
Runtime vs Compile Time
TypeScript valida en tiempo de compilación, no en runtime.
type User = {
name: string;
};
const user = JSON.parse('{"name": 123}') as User;
user.name.toUpperCase(); // runtime error: name es number
Para validar datos externos necesitás validación runtime: Zod, Valibot o io-ts.
Frases útiles para entrevistas
unknown
“
unknownobliga a hacer narrowing antes de usar el valor.”
type vs interface
“Uso
interfacepara contratos extensibles ytypepara composición avanzada y unions.”
Discriminated Unions
“Permiten modelar estados válidos e invalidar estados imposibles.”
Generics
“Los genéricos permiten reutilizar lógica sin perder inferencia de tipos.”
as const
“Preserva literal types y evita widening.”
Type Assertions
“Las type assertions no validan runtime; solo cambian cómo TypeScript interpreta el valor.”