
Sintaxis de anotaciones de tipo
Una anotación de tipo indica al compilador el tipo de dato que puede contener una variable, un parámetro o un valor de retorno. La sintaxis consiste en dos puntos seguidos del nombre del tipo, colocados después del identificador:
let nombre: string = "Elena";
let edad: number = 30;
let activo: boolean = true;
console.log(nombre, edad, activo);
La anotación : string declara que la variable nombre solo puede contener valores de tipo cadena de texto. Si se intenta asignar un valor de otro tipo, el compilador genera un error:
let precio: number = 49.99;
// Error: Type 'string' is not assignable to type 'number'
// precio = "cuarenta y nueve";
console.log(precio);
Las anotaciones de tipo solo existen durante la compilación. El JavaScript generado no contiene ninguna información de tipos.
Anotaciones en funciones
En las funciones, las anotaciones se aplican a cada parámetro y al valor de retorno:
function calcularImpuesto(base: number, porcentaje: number): number {
return base * (porcentaje / 100);
}
const impuesto = calcularImpuesto(1000, 21);
console.log(impuesto);
El tipo de retorno se coloca después del paréntesis de cierre de los parámetros. Si la función no devuelve ningun valor, se utiliza void:
function mostrarMensaje(texto: string): void {
console.log(texto);
}
mostrarMensaje("Operación completada");
Anotaciones en funciones flecha
Las funciones flecha siguen la misma sintaxis:
const duplicar = (n: number): number => n * 2;
const saludar = (nombre: string): string => {
return `Hola, ${nombre}`;
};
console.log(duplicar(5));
console.log(saludar("Carlos"));
Cuando se asigna una función flecha a una variable con tipo declarado, los parámetros pueden omitir sus anotaciones porque TypeScript los deduce del contexto:
type Operación = (a: number, b: number) => number;
const sumar: Operación = (a, b) => a + b;
const restar: Operación = (a, b) => a - b;
console.log(sumar(10, 3));
console.log(restar(10, 3));
Tipos primitivos
TypeScript reconoce todos los tipos primitivos de JavaScript y permite usarlos como anotaciones de tipo.
string, number y boolean
Los tres tipos primitivos más comunes:
const titulo: string = "Aprende TypeScript";
const cantidad: number = 42;
const disponible: boolean = true;
console.log(titulo, cantidad, disponible);
El tipo number abarca enteros, decimales, hexadecimales, binarios y octales:
const entero: number = 100;
const decimal: number = 3.14;
const hexadecimal: number = 0xff;
const binario: number = 0b1010;
const octal: number = 0o744;
console.log(entero, decimal, hexadecimal, binario, octal);
null y undefined
En TypeScript, null y undefined son tipos propios. Con la opción strictNullChecks activada, no se pueden asignar a otros tipos sin declararlo explícitamente:
let valor: string | null = "texto";
valor = null; // Valido porque el tipo incluye null
let opcional: number | undefined = undefined;
opcional = 42; // Valido
console.log(valor, opcional);
Sin la union explícita, asignar null o undefined a una variable de otro tipo genera un error:
let nombre: string = "Ana";
// Error: Type 'null' is not assignable to type 'string'
// nombre = null;
console.log(nombre);
Con strictNullChecks activado, TypeScript obliga a manejar explícitamente los casos donde un valor puede ser null o undefined. Esto previene la categoría más común de errores en JavaScript.
symbol
El tipo symbol representa valores únicos e inmutables, creados con la función Symbol():
const id: symbol = Symbol("identificador");
const otro: symbol = Symbol("identificador");
console.log(id === otro); // false: cada Symbol es único
console.log(typeof id);
bigint
El tipo bigint permite trabajar con enteros de precisión arbitraria, útiles para cálculos que exceden el rango seguro de number:
const grande: bigint = 9007199254740991n;
const suma: bigint = grande + 1n;
console.log(suma);
console.log(typeof suma);
Los valores bigint se crean con el sufijo n después del literal numérico. No se pueden mezclar number y bigint en operaciones aritmeticas.
any y unknown
TypeScript proporciona dos tipos especiales para situaciones donde el tipo exacto no se conoce. Aunque ambos aceptan cualquier valor, su comportamiento es fundamentalmente diferente.
El tipo any
El tipo any desactiva completamente la verificación de tipos. Una variable de tipo any se puede usar como si fuera de cualquier tipo, sin que el compilador genere errores:
let dato: any = "texto";
dato = 42;
dato = true;
dato = { clave: "valor" };
// Todo esto es válido con any, aunque sea peligroso
console.log(dato.metodoInexistente);
console.log(dato.propiedad.anidada);
El compilador no verifica ninguna operación sobre variables any. Esto significa que los errores solo se detectaran en tiempo de ejecución, anulando la principal ventaja de TypeScript.
function procesarAny(datos: any): any {
// TypeScript no verifica nada aquí
return datos.nombre.toUpperCase();
}
// Compila sin errores, pero falla en ejecución si datos no tiene .nombre
console.log(procesarAny({ nombre: "Ana" }));
El tipo any debe evitarse en código nuevo. Existe principalmente para facilitar la migración gradual desde JavaScript y para interactuar con bibliotecas sin definiciones de tipos.
El tipo unknown
El tipo unknown es la alternativa segura a any. Acepta cualquier valor, pero obliga a verificar el tipo antes de utilizarlo:
let entrada: unknown = "texto";
// Error: 'entrada' is of type 'unknown'
// console.log(entrada.toUpperCase());
// Solucion: verificar el tipo primero
if (typeof entrada === "string") {
console.log(entrada.toUpperCase()); // TypeScript sabe que es string aquí
}
Con unknown, TypeScript exige una comprobación explícita antes de permitir cualquier operación específica del tipo:
function procesarEntrada(valor: unknown): string {
if (typeof valor === "string") {
return valor.toUpperCase();
}
if (typeof valor === "number") {
return valor.toFixed(2);
}
return String(valor);
}
console.log(procesarEntrada("hola"));
console.log(procesarEntrada(3.14159));
console.log(procesarEntrada(true));
Comparación práctica entre any y unknown
// Con any: peligroso, no hay verificación
function parsearJSON_any(texto: string): any {
return JSON.parse(texto);
}
const datos_any = parsearJSON_any('{"nombre": "Ana"}');
console.log(datos_any.nombre); // Funciona, pero sin verificación
// datos_any.metodoFalso(); // Compilaria sin error, falla en ejecución
// Con unknown: seguro, requiere verificación
function parsearJSON_unknown(texto: string): unknown {
return JSON.parse(texto);
}
const datos_unknown = parsearJSON_unknown('{"nombre": "Ana"}');
// console.log(datos_unknown.nombre); // Error: Object is of type 'unknown'
// Se necesita verificación
if (typeof datos_unknown === "object" && datos_unknown !== null && "nombre" in datos_unknown) {
console.log((datos_unknown as { nombre: string }).nombre);
}
El tipo void
El tipo void indica que una función no devuelve ningun valor significativo. Es el tipo de retorno implícito de funciones que no tienen sentencia return o que usan return sin valor:
function registrar(mensaje: string): void {
console.log(`[LOG] ${mensaje}`);
}
function notificar(usuario: string, evento: string): void {
console.log(`Notificacion para ${usuario}: ${evento}`);
return; // return sin valor es válido con void
}
registrar("Inicio de la aplicación");
notificar("Ana", "nuevo mensaje");
Declarar variables de tipo void no es útil en la práctica. El uso principal de void es como tipo de retorno de funciones y como tipo de retorno en firmas de callbacks:
type Callback = (resultado: string) => void;
function ejecutarConCallback(tarea: string, cb: Callback): void {
const resultado = `Tarea "${tarea}" completada`;
cb(resultado);
}
ejecutarConCallback("compilar", (msg) => {
console.log(msg);
});
Inferencia de tipos
TypeScript no requiere anotar cada variable y cada retorno. El motor de inferencia deduce automáticamente los tipos cuando hay suficiente información contextual. Esto reduce la cantidad de anotaciones necesarias sin perder seguridad.
Inferencia en variables
Cuando una variable se inicializa con un valor, TypeScript infiere el tipo a partir de ese valor:
const mensaje = "Hola mundo"; // tipo inferido: string
const cantidad = 100; // tipo inferido: number (con let sería number)
const activo = true; // tipo inferido: boolean (con let sería boolean)
const elementos = [1, 2, 3]; // tipo inferido: number[]
const mixto = [1, "dos", true]; // tipo inferido: (string | number | boolean)[]
console.log(mensaje, cantidad, activo, elementos, mixto);
Con const, TypeScript infiere tipos literales más estrechos para primitivos:
const estado = "activo"; // tipo: "activo" (literal)
let fase = "inicio"; // tipo: string (mas amplio, porque let permite reasignacion)
console.log(estado, fase);
Inferencia en funciones
El tipo de retorno de una función se infiere a partir de sus sentencias return:
function calcularDescuento(precio: number, porcentaje: number) {
return precio * (1 - porcentaje / 100); // retorno inferido: number
}
function obtenerEtiqueta(código: number) {
if (código === 1) return "activo";
if (código === 2) return "inactivo";
return "desconocido"; // retorno inferido: string
}
console.log(calcularDescuento(100, 15));
console.log(obtenerEtiqueta(1));
Los parámetros de funciones no se infieren y deben anotarse (excepto en contextos donde TypeScript tiene información del tipo esperado, como callbacks):
const números = [10, 20, 30, 40];
// El parámetro 'n' se infiere como number porque números es number[]
const dobles = números.map(n => n * 2);
console.log(dobles);
// El parámetro 'n' se infiere como number, el parámetro 'i' como number
const indexados = números.map((n, i) => `${i}: ${n}`);
console.log(indexados);
Cuándo anotar y cuándo dejar que infiera
La recomendación práctica es:
- Anotar siempre los parámetros de funciones públicas y los tipos de retorno complejos.
- Dejar que infiera los tipos de variables locales cuando el tipo es evidente por el valor asignado.
- Anotar explícitamente cuando la inferencia produce un tipo más amplio del deseado.
// Anotación necesaria: parámetros de función
function filtrarPorMinimo(valores: number[], minimo: number): number[] {
return valores.filter(v => v >= minimo);
}
// Anotación innecesaria: el tipo es evidente
const resultado = filtrarPorMinimo([5, 10, 15, 20], 12);
console.log(resultado);
// Anotación útil: la inferencia sería demasiado amplia
const configuración: { modo: "desarrollo" | "producción"; puerto: number } = {
modo: "desarrollo",
puerto: 3000
};
console.log(configuración);
Inferencia contextual
TypeScript utiliza el contexto para inferir tipos en funciones anónimas y callbacks. Cuando una función se pasa como argumento a otra función cuyo tipo de parámetro está definido, los tipos se propagan automáticamente:
type Transformador = (valor: string) => string;
function aplicar(textos: string[], fn: Transformador): string[] {
return textos.map(fn);
}
// Los parámetros de la función anonima se infieren del tipo Transformador
const mayusculas = aplicar(["hola", "mundo"], (valor) => valor.toUpperCase());
console.log(mayusculas);
// También funciona con métodos de array
const nombres = ["Ana", "Carlos", "Elena"];
const longitudes = nombres.map(nombre => nombre.length); // nombre inferido como string
console.log(longitudes);
La inferencia contextual es una de las razones por las que TypeScript resulta menos verboso de lo que podría parecer. En muchas situaciones, el compilador tiene suficiente información para deducir los tipos sin anotaciones explícitas.
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
Utilizar la sintaxis de anotaciones de tipo con : type en variables, parámetros y valores de retorno. Conocer los tipos primitivos de TypeScript: string, number, boolean, null, undefined, symbol y bigint. Comprender la diferencia entre any y unknown y cuando usar cada uno. Aplicar la inferencia de tipos para reducir anotaciones innecesarias. Utilizar void para funciones que no devuelven valor.