El sistema de tipado estático de TypeScript se construye sobre el runtime de JavaScript como una capa borrable (Type Erasure). Todos los tipos, interfaces y anotaciones existen exclusivamente durante la fase de compilación en el AST (Abstract Syntax Tree) y se eliminan por completo al transpilarlos a JavaScript puro. Esto significa una sobrecarga nula en tiempo de ejecución: los tipos no aumentan el tamaño del bundle ni ralentizan la ejecución del programa.
Los tipos primitivos son el fundamento del sistema: string, number, boolean, bigint, symbol, null y undefined. A diferencia de las envolturas de objeto primitivas de JavaScript (String, Number, Boolean), los tipos primitivos en TypeScript se escriben estrictamente con minúscula y representan valores atómicos indivisibles.
Un mecanismo clave de la ergonomía de TypeScript es la inferencia automática de tipos (Type Inference). El compilador utiliza el análisis de flujo de control contextual (Control Flow Analysis) y un algoritmo de unificación para determinar de forma autónoma el tipo seguro más específico de una variable sin la necesidad de una anotación manual excesiva.
En TypeScript, existe una división fundamental entre variables mutables (let, var) y constantes (const). Esta división determina directamente la estrategia de inferencia del compilador:
+-------------------------------------------------------------------------+
| MODELO MENTAL DE INFERENCIA DE TIPOS (Inference) |
+-------------------------------------------------------------------------+
| let status = 'pending' ---> Tipo: string (Type Widening) |
| (Puede cambiar en el futuro) Permite asignar cualquier cadena |
+-------------------------------------------------------------------------+
| const status = 'pending' ---> Tipo: 'pending' (Literal Type) |
| (Nunca cambiará) Subtipo estrecho de string de una sola palabra |
+-------------------------------------------------------------------------+
| const config = { port: 3000 } ---> Tipo: { port: number } |
| (El objeto es constante por referencia, ¡Las propiedades son mutables! |
| pero los campos pueden ser reescritos) Se necesita "as const" para una inmutabilidad profunda |
+-------------------------------------------------------------------------+as const)
El parser transforma el código fuente en un AST. La expresión de inicialización se vincula con el identificador de la variable.
El compilador analiza el modificador de declaración: para `const` se calcula un tipo literal, para `let` se aplica Type Widening.
En operaciones posteriores, el compilador compara el tipo del nuevo valor con el tipo inferido según las reglas de compatibilidad estructural.
El emisor del compilador elimina todas las anotaciones y genera código JS puro sin ningún polyfill de verificación de tipos.
¿Qué tipo obtendrá la variable `theme` en la siguiente expresión: `const theme = 'dark'`?
'GET' | 'POST' | 'PUT' | 'DELETE' en lugar del tipo string genérico.variant: 'primary' | 'secondary' | 'ghost').symbol y bigint para transacciones financieras de alta precisión y claves globales únicas.as const, eliminando errores tipográficos en los segmentos de URL durante la compilación.En JavaScript existen primitivas ('hello', 42, true) y sus correspondientes envolturas de objeto (new String('hello')). En TypeScript, el uso de tipos con mayúscula inicial String, Number, Boolean, Object se considera un error grave:
// CORRECTO: uso de tipos primitivos
const username: string = 'alex_dev';
const retryCount: number = 3;
const isProduction: boolean = true;
const transactionId: bigint = 9007199254740991n;
const uniqueKey: symbol = Symbol('node_id');
// INCORRECTO: ¡los tipos de envoltura describen instancias de clases, no valores primitivos!
// let badString: String = 'bad'; // ¡Antipatrón!El compilador estrecha automáticamente el tipo al usar const y lo amplía al usar let:
let userRole = 'admin'; // Tipo: string (se puede reasignar userRole = 'viewer')
const defaultRole = 'admin'; // Tipo: 'admin' (tipo literal)
function assignPermission(role: 'admin' | 'moderator') {
console.log(`Access granted for ${role}`);
}
assignPermission(defaultRole); // Correcto: 'admin' es compatible con 'admin' | 'moderator'
// ERROR DE COMPILACIÓN:
// assignPermission(userRole);
// TS2345: Argument of type 'string' is not assignable to parameter of type ''admin' | 'moderator''.as const#Por defecto, los campos de los objetos se amplían (widening), incluso si el objeto mismo se declara como const:
// Sin 'as const': las propiedades son mutables
const defaultSettings = {
endpoint: 'https://api.wpskills.dev/v1',
timeout: 5000,
logLevel: 'info', // ¡Tipo de campo: string!
};
// defaultSettings.logLevel = 'debug'; // ¡Permitido por el compilador!
// Con 'as const': readonly profundo y tipos literales
const safeSettings = {
endpoint: 'https://api.wpskills.dev/v1',
timeout: 5000,
logLevel: 'info',
} as const;
// Tipo safeSettings:
// {
// readonly endpoint: "https://api.wpskills.dev/v1";
// readonly timeout: 5000;
// readonly logLevel: "info";
// }
// safeSettings.timeout = 10000; // ERROR TS2540: Cannot assign to 'timeout' because it is a read-only property.¿Por qué el compilador emite un error al pasar `config.method` a la función `fetchData(config.url, config.method)` si `method: 'POST'` está dentro del objeto `const config = { url: '/api', method: 'POST' }`?
Los tipos con mayúscula inicial String, Number, Boolean no describen primitivas, sino instancias de constructores JS incorporados. Un valor primitivo no es una instancia completa de Object, lo que causa errores de compatibilidad inesperados:
// ERROR:
function format(id: String) { /* ... */ }
// CORRECTO:
function format(id: string) { /* ... */ }Utilice siempre y exclusivamente los nombres de las primitivas en minúscula.
Escribir let count: number = 0 o const name: string = 'Alice' es ruido en el código. El compilador calcula los tipos de expresiones triviales sin error. Anote los tipos solo donde la inferencia no sea posible (parámetros de funciones, arrays vacíos, tipos de retorno complejos).
Declarar una variable como any desactiva la verificación de tipos del compilador para todo el flujo de operaciones subsiguiente:
// PELIGRO: ¡el compilador guarda silencio, la aplicación falla en tiempo de ejecución!
const rawPayload: any = JSON.parse('{"status": 200}');
rawPayload.unknownMethod(); // ¡TypeError en tiempo de ejecución!
// CORRECTO: use `unknown` con validación posterior
const safePayload: unknown = JSON.parse('{"status": 200}');