Tipos primitivos básicos
TypeScript hereda todos los tipos primitivos de JavaScript y añade un sistema de tipado estático que permite detectar errores en tiempo de compilación. Los tipos primitivos son los bloques fundamentales para construir aplicaciones más complejas y representan los valores más básicos que podemos manejar.
El tipo number
El tipo number
en TypeScript representa tanto números enteros como decimales, siguiendo el estándar IEEE 754 de punto flotante de doble precisión. A diferencia de otros lenguajes, TypeScript no distingue entre enteros y decimales.
let edad: number = 25;
let precio: number = 19.99;
let temperatura: number = -15;
let cientifico: number = 1.23e-4;
TypeScript también soporta literales numéricos en diferentes bases:
let decimal: number = 42;
let hexadecimal: number = 0x2A;
let binario: number = 0b101010;
let octal: number = 0o52;
El tipo string
Las cadenas de texto se definen con el tipo string
y pueden crearse usando comillas simples, dobles o template literals:
let nombre: string = "María";
let apellido: string = 'González';
let saludo: string = `Hola, ${nombre} ${apellido}`;
Los template literals son especialmente útiles para crear cadenas dinámicas:
let usuario: string = "admin";
let intentos: number = 3;
let mensaje: string = `Usuario ${usuario} tiene ${intentos} intentos restantes`;
El tipo boolean
El tipo boolean
solo puede tener dos valores: true
o false
. Es fundamental para la lógica condicional:
let estaActivo: boolean = true;
let esValido: boolean = false;
let resultado: boolean = edad >= 18;
El tipo bigint
Para números enteros muy grandes que exceden el límite seguro de number
(2^53 - 1), TypeScript proporciona el tipo bigint
:
let numeroGrande: bigint = 9007199254740991n;
let otroGrande: bigint = BigInt("9007199254740992");
Es importante recordar que no se pueden mezclar operaciones entre number
y bigint
:
let normal: number = 100;
let grande: bigint = 200n;
// Error: no se pueden sumar directamente
// let suma = normal + grande;
// Correcto: convertir tipos
let suma: bigint = BigInt(normal) + grande;
El tipo symbol
Los símbolos son primitivos únicos e inmutables, útiles como identificadores de propiedades de objetos:
let id1: symbol = Symbol("id");
let id2: symbol = Symbol("id");
// Aunque tengan la misma descripción, son diferentes
console.log(id1 === id2); // false
Los símbolos garantizan unicidad absoluta, incluso cuando se crean con la misma descripción:
let claveUnica: symbol = Symbol("configuracion");
let otraClaveUnica: symbol = Symbol("configuracion");
// Son completamente diferentes
console.log(claveUnica === otraClaveUnica); // false
Inferencia automática de tipos
TypeScript puede inferir automáticamente el tipo de una variable basándose en su valor inicial:
// TypeScript infiere que es number
let contador = 0;
// TypeScript infiere que es string
let mensaje = "Bienvenido";
// TypeScript infiere que es boolean
let activo = true;
Sin embargo, es recomendable especificar explícitamente los tipos en casos donde la inferencia podría ser ambigua o cuando queremos ser más claros sobre nuestras intenciones:
// Explícito: más claro para otros desarrolladores
let porcentaje: number = 0;
let estado: boolean = false;
Conversión entre tipos primitivos
TypeScript permite conversiones explícitas entre tipos primitivos usando las funciones constructoras correspondientes:
let texto: string = "42";
let numero: number = Number(texto);
let booleano: boolean = Boolean(numero);
// También podemos usar parseInt y parseFloat
let entero: number = parseInt("123.45");
let decimal: number = parseFloat("123.45");
La conversión a string es especialmente común:
let edad: number = 25;
let edadTexto: string = String(edad);
let mensaje: string = `Tienes ${edad} años`; // Conversión automática
Buenas prácticas con tipos primitivos
Siempre utiliza anotaciones de tipo explícitas en parámetros de funciones y propiedades de objetos, incluso si TypeScript puede inferirlas:
function calcularDescuento(precio: number, porcentaje: number): number {
return precio * (porcentaje / 100);
}
function saludar(nombre: string): string {
return `Hola, ${nombre}`;
}
Evita el uso de constructores de objetos para tipos primitivos:
// Incorrecto: crea objetos, no primitivos
let texto = new String("hola");
let numero = new Number(42);
// Correcto: valores primitivos
let texto: string = "hola";
let numero: number = 42;
Tipos especiales
TypeScript incluye varios tipos especiales que van más allá de los primitivos básicos. Estos tipos proporcionan mayor flexibilidad y control sobre el sistema de tipos, permitiendo manejar situaciones específicas donde los tipos primitivos no son suficientes.
El tipo any
El tipo any
desactiva completamente la verificación de tipos para una variable, permitiendo asignar cualquier valor y acceder a cualquier propiedad:
let valor: any = 42;
valor = "texto";
valor = true;
valor = { nombre: "Juan" };
// No hay verificación de tipos
valor.propiedad.inexistente.anidada; // No genera error
Aunque any
proporciona máxima flexibilidad, debe usarse con precaución ya que elimina los beneficios del tipado estático:
function procesarDatos(datos: any): any {
// Perdemos toda la información de tipos
return datos.toUpperCase(); // ¿Qué pasa si datos no es string?
}
Casos de uso legítimos para any
:
- Migración gradual de código JavaScript existente
- Integración con bibliotecas de terceros sin tipado
- Prototipado rápido donde el tipado se refinará después
El tipo unknown
El tipo unknown
es la alternativa segura a any
. Representa un valor de tipo desconocido, pero requiere verificaciones antes de su uso:
let valor: unknown = obtenerDatosExternos();
// Error: no podemos usar directamente unknown
// console.log(valor.length);
// Correcto: verificamos el tipo primero
if (typeof valor === "string") {
console.log(valor.length); // TypeScript sabe que es string
}
Unknown
es especialmente útil cuando recibimos datos externos y queremos validarlos antes de procesarlos:
function procesarRespuestaAPI(respuesta: unknown): string {
if (typeof respuesta === "string") {
return respuesta.toUpperCase();
}
if (typeof respuesta === "object" && respuesta !== null) {
return JSON.stringify(respuesta);
}
return "Datos no válidos";
}
El tipo void
El tipo void
indica la ausencia de un valor de retorno. Se usa principalmente en funciones que no devuelven nada:
function mostrarMensaje(texto: string): void {
console.log(texto);
// No hay return explícito
}
function configurarEventos(): void {
document.addEventListener("click", () => {
console.log("Click detectado");
});
}
Una variable de tipo void
solo puede ser undefined
o null
:
let resultado: void = undefined; // Válido
// let otro: void = "texto"; // Error
El tipo never
El tipo never
representa valores que nunca ocurren. Se usa en funciones que nunca terminan o siempre lanzan excepciones:
function lanzarError(mensaje: string): never {
throw new Error(mensaje);
// Esta línea nunca se ejecuta
}
function bucleInfinito(): never {
while (true) {
console.log("Ejecutándose...");
}
// Esta función nunca termina
}
Never
también aparece en análisis de flujo exhaustivo:
type Estado = "cargando" | "completado" | "error";
function manejarEstado(estado: Estado): string {
switch (estado) {
case "cargando":
return "Procesando...";
case "completado":
return "Finalizado";
case "error":
return "Ha ocurrido un error";
default:
// estado es de tipo never aquí
const _exhaustivo: never = estado;
throw new Error(`Estado no manejado: ${estado}`);
}
}
Los tipos null y undefined
null
y undefined
son tipos y valores distintos en TypeScript:
let valorNulo: null = null;
let valorIndefinido: undefined = undefined;
// Son tipos diferentes
let opcional: string | null = null;
let sinAsignar: string | undefined = undefined;
Por defecto, null
y undefined
son subtipos de todos los demás tipos:
let texto: string = null; // Válido por defecto
let numero: number = undefined; // Válido por defecto
Con strictNullChecks
habilitado (recomendado), debemos ser explícitos:
// Con strictNullChecks: true
let nombre: string = "Juan";
// nombre = null; // Error
let nombreOpcional: string | null = "María";
nombreOpcional = null; // Válido
El tipo object
El tipo object
representa cualquier valor que no sea un primitivo:
let persona: object = { nombre: "Ana", edad: 30 };
let fechas: object = new Date();
let numeros: object = [1, 2, 3];
// Error: los primitivos no son object
// let texto: object = "hola";
// let numero: object = 42;
Sin embargo, object
es muy genérico y limitado. Generalmente preferimos interfaces o tipos más específicos:
// Mejor que object genérico
interface Usuario {
nombre: string;
edad: number;
}
let usuario: Usuario = { nombre: "Carlos", edad: 25 };
Verificación de tipos especiales
Para trabajar con tipos especiales de forma segura, usamos verificaciones de tipo:
function procesarValor(valor: unknown): string {
// Verificación de null/undefined
if (valor == null) {
return "Valor nulo o indefinido";
}
// Verificación de tipo primitivo
if (typeof valor === "string") {
return valor.toUpperCase();
}
// Verificación de objeto
if (typeof valor === "object") {
return JSON.stringify(valor);
}
return String(valor);
}
Buenas prácticas con tipos especiales
Evita any
siempre que sea posible. Si necesitas flexibilidad, considera unknown
:
// Evitar
function procesar(datos: any): any {
return datos.resultado;
}
// Preferir
function procesarSeguro(datos: unknown): string {
if (typeof datos === "object" && datos !== null && "resultado" in datos) {
return String((datos as any).resultado);
}
throw new Error("Datos inválidos");
}
Usa void
consistentemente en funciones sin retorno:
// Correcto
function actualizar(): void {
// lógica de actualización
}
// Evitar retorno implícito ambiguo
function configurar() {
// sin anotación de tipo
}
Aprovecha never
para crear código más robusto:
function validarTipo(valor: string | number): string {
if (typeof valor === "string") {
return valor.trim();
}
if (typeof valor === "number") {
return valor.toString();
}
// Esta línea garantiza que hemos cubierto todos los casos
const _never: never = valor;
throw new Error("Tipo no esperado");
}
Tipos literales y const assertions
Los tipos literales en TypeScript permiten especificar valores exactos como tipos, proporcionando un control más granular sobre qué valores son aceptables. Esta característica, combinada con las const assertions, ofrece herramientas avanzadas para crear código más preciso y seguro.
Tipos literales de cadena
Un tipo literal de cadena especifica que una variable solo puede contener un valor de cadena específico:
let direccion: "norte" = "norte";
// direccion = "sur"; // Error: solo acepta "norte"
let estado: "activo" | "inactivo" | "pendiente" = "activo";
estado = "inactivo"; // Válido
// estado = "otro"; // Error: no está en la unión
Los tipos literales son especialmente útiles para crear enumeraciones de cadenas sin usar la palabra clave enum
:
type TipoNotificacion = "info" | "warning" | "error" | "success";
function mostrarNotificacion(tipo: TipoNotificacion, mensaje: string): void {
console.log(`[${tipo.toUpperCase()}] ${mensaje}`);
}
mostrarNotificacion("info", "Proceso completado"); // Válido
// mostrarNotificacion("debug", "Error"); // Error: "debug" no es válido
Tipos literales numéricos
Los números específicos también pueden usarse como tipos literales:
let puerto: 3000 | 8080 | 9000 = 3000;
puerto = 8080; // Válido
// puerto = 5000; // Error: no está permitido
type CodigoHTTP = 200 | 404 | 500 | 503;
function manejarRespuesta(codigo: CodigoHTTP): string {
switch (codigo) {
case 200:
return "Éxito";
case 404:
return "No encontrado";
case 500:
return "Error interno";
case 503:
return "Servicio no disponible";
}
}
Tipos literales booleanos
Aunque menos comunes, los valores booleanos específicos también pueden ser tipos literales:
let configuracionDebug: true = true;
// configuracionDebug = false; // Error: solo acepta true
type ConfiguracionFeature = {
habilitado: true; // Solo acepta true
experimental: false; // Solo acepta false
};
Const assertions básicas
Las const assertions utilizan la sintaxis as const
para indicar a TypeScript que trate un valor como inmutable y con tipos más específicos:
// Sin const assertion - tipo inferido: string
let mensaje1 = "bienvenido";
// Con const assertion - tipo inferido: "bienvenido"
let mensaje2 = "bienvenido" as const;
Esta diferencia es crucial cuando trabajamos con valores que no deben cambiar:
// Tipo inferido: string
const saludo = "hola";
// Tipo inferido: "hola" (literal)
const saludoLiteral = "hola" as const;
function procesar(texto: "hola" | "adios"): void {
console.log(texto);
}
// procesar(saludo); // Error: string no es asignable a "hola" | "adios"
procesar(saludoLiteral); // Válido: "hola" es exactamente lo esperado
Const assertions con objetos
Las const assertions en objetos convierten todas las propiedades en readonly
y sus valores en tipos literales:
// Sin const assertion
const configuracion1 = {
servidor: "localhost",
puerto: 3000,
ssl: false
};
// Tipo: { servidor: string; puerto: number; ssl: boolean; }
// Con const assertion
const configuracion2 = {
servidor: "localhost",
puerto: 3000,
ssl: false
} as const;
// Tipo: { readonly servidor: "localhost"; readonly puerto: 3000; readonly ssl: false; }
Esto es especialmente útil para configuraciones que no deben modificarse:
const CONFIGURACION_API = {
baseUrl: "https://api.ejemplo.com",
version: "v1",
timeout: 5000
} as const;
// Error: no se puede modificar
// CONFIGURACION_API.baseUrl = "otra-url";
// Podemos extraer tipos de la configuración
type ConfiguracionAPI = typeof CONFIGURACION_API;
Narrowing con tipos literales
Los tipos literales permiten que TypeScript realice narrowing automático en estructuras condicionales:
type EstadoProceso = "iniciando" | "procesando" | "completado" | "error";
function manejarProceso(estado: EstadoProceso): string {
if (estado === "iniciando") {
// TypeScript sabe que estado es exactamente "iniciando"
return "Preparando proceso...";
}
if (estado === "procesando") {
// TypeScript sabe que estado es exactamente "procesando"
return "Proceso en ejecución...";
}
// TypeScript sabe que aquí estado es "completado" | "error"
return estado === "completado" ? "Finalizado" : "Error detectado";
}
Const assertions con estructuras anidadas
Las const assertions se aplican recursivamente a estructuras anidadas:
const menuNavegacion = {
principal: {
inicio: "/",
productos: "/productos",
contacto: "/contacto"
},
admin: {
dashboard: "/admin",
usuarios: "/admin/usuarios"
}
} as const;
// Todos los valores son literales y readonly
type RutaInicio = typeof menuNavegacion.principal.inicio; // "/"
type RutasAdmin = typeof menuNavegacion.admin[keyof typeof menuNavegacion.admin]; // "/admin" | "/admin/usuarios"
Template literal types
TypeScript permite crear tipos literales con plantillas que combinan literales de cadena:
type Prefijo = "get" | "set" | "delete";
type Recurso = "User" | "Product" | "Order";
// Combinación de literales usando template literals
type MetodoAPI = `${Prefijo}${Recurso}`;
// Resultado: "getUser" | "setUser" | "deleteUser" | "getProduct" | "setProduct" | "deleteProduct" | "getOrder" | "setOrder" | "deleteOrder"
function ejecutarMetodo(metodo: MetodoAPI): void {
console.log(`Ejecutando: ${metodo}`);
}
ejecutarMetodo("getUser"); // Válido
ejecutarMetodo("setProduct"); // Válido
// ejecutarMetodo("createUser"); // Error: no está en el tipo
Casos prácticos con tipos literales
Los tipos literales son ideales para APIs con valores específicos:
type MetodoHTTP = "GET" | "POST" | "PUT" | "DELETE";
type ContentType = "application/json" | "application/xml" | "text/plain";
interface ConfiguracionRequest {
metodo: MetodoHTTP;
contentType: ContentType;
url: string;
}
function hacerRequest(config: ConfiguracionRequest): void {
// TypeScript garantiza que método y contentType son valores válidos
console.log(`${config.metodo} ${config.url} (${config.contentType})`);
}
También son útiles para sistemas de eventos tipados:
type EventoUsuario = "login" | "logout" | "registro" | "actualizacion";
const manejadoresEventos = {
login: () => console.log("Usuario conectado"),
logout: () => console.log("Usuario desconectado"),
registro: () => console.log("Nuevo usuario registrado"),
actualizacion: () => console.log("Perfil actualizado")
} as const;
function procesarEvento(evento: EventoUsuario): void {
const manejador = manejadoresEventos[evento];
manejador();
}
Buenas prácticas con tipos literales
Prefiere tipos literales sobre strings genéricos cuando tengas un conjunto conocido de valores:
// Evitar
function cambiarTema(tema: string): void {
// No sabemos qué valores son válidos
}
// Preferir
type Tema = "claro" | "oscuro" | "automatico";
function cambiarTema(tema: Tema): void {
// TypeScript valida que el tema sea correcto
}
Usa const assertions para configuraciones inmutables:
const CONFIGURACION_PRODUCCION = {
apiUrl: "https://api.produccion.com",
cacheTTL: 3600,
debugMode: false
} as const;
// Exportar tipo derivado para reutilización
export type ConfiguracionProduccion = typeof CONFIGURACION_PRODUCCION;
Combina tipos literales con validación en tiempo de ejecución:
type NivelLog = "debug" | "info" | "warn" | "error";
function esNivelLogValido(valor: string): valor is NivelLog {
return ["debug", "info", "warn", "error"].includes(valor);
}
function configurarLogger(nivel: string): void {
if (esNivelLogValido(nivel)) {
// nivel es ahora de tipo NivelLog
console.log(`Logger configurado en nivel: ${nivel}`);
} else {
throw new Error(`Nivel de log inválido: ${nivel}`);
}
}
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en TypeScript
Documentación oficial de TypeScript
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, TypeScript es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de TypeScript
Explora más contenido relacionado con TypeScript y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender los tipos primitivos básicos en TypeScript: number, string, boolean, bigint y symbol.
- Conocer los tipos especiales como any, unknown, void, never, null, undefined y object.
- Aprender el uso y ventajas de los tipos literales y las const assertions para mayor precisión en el tipado.
- Aplicar buenas prácticas en la anotación y manejo de tipos para mejorar la seguridad y claridad del código.
- Entender cómo realizar conversiones y verificaciones de tipos de forma segura y eficiente.