Menu

Tipos literales y as const en TypeScript, con ejemplos

Un tipo literal es un tipo con exactamente un valor, como "GET" o 404. Aprende los tipos literales de string, number y boolean, las uniones de literales, por qué let ensancha y const no, qué hace as const y los parámetros de tipo const.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Un tipo literal es un tipo con exactamente un valor: "up" es un tipo cuyo único miembro es el string "up", y 404 es un tipo cuyo único miembro es el número 404. Por sí solos no son muy útiles. Unidos en una unión, te dan una variable que acepta un conjunto fijo de valores y nada más.

Sin el comentario @ts-expect-error, la última llamada es un error en tiempo de compilación (TS2345). Con él, el programa compila y la llamada se ejecuta igualmente e imprime moving north by 1: los tipos literales solo existen para el compilador, y en tiempo de ejecución el valor es un string normal.

Literales de string, number y boolean

Cualquier valor string, number, bigint o boolean se puede escribir como tipo. El compilador acepta entonces solo ese valor exacto.

let method: "GET" = "GET";
let code: 404 = 404;
let ok: true = true;
let big: 10n = 10n;

type Port = 80 | 443 | 8080;
const port: Port = 443;

boolean en sí es solo la unión true | false, y por eso estrechar un boolean con if (flag) deja false en la rama else.

Tipo literalAdmiteTipo más amplio
"GET"solo el string "GET"string
404solo el número 404number
10nsolo el bigint 10bigint
truesolo trueboolean

Uniones de literales

El uso habitual es una unión que enumera todos los valores permitidos. Dentro de la función, el compilador estrecha la unión a medida que la compruebas, así que cada rama sabe exactamente qué valor tiene.

Una unión de literales de string es la alternativa habitual a un enum en TypeScript. No cuesta nada en tiempo de ejecución, los valores son strings normales que puedes escribir en logs y enviar por JSON, y una errata es un error de compilación. Hay más sobre las ventajas e inconvenientes en enums.

Ensanchamiento: let frente a const

Cuando TypeScript infiere un tipo a partir de un literal, mira si el valor puede cambiar. Una variable const no se puede reasignar nunca, así que conserva el tipo literal. Una variable let sí, así que su tipo se ensancha al tipo general.

Pasa el cursor por cada nombre en el editor para ver el tipo inferido. Si quieres un let que solo guarde ciertos valores, anótalo: let mode: "light" | "dark" = "light".

Por qué se ensanchan las propiedades de los objetos

Las propiedades de un objeto literal son mutables, así que también se ensanchan, aunque el objeto esté guardado en una const. Es la forma más habitual de toparse con los tipos literales sin querer:

El compilador informa:

index.ts(7,15): error TS2345: Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

req se infiere como { url: string; method: string } porque más adelante algún código podría ejecutar req.method = "DELETE". Hay tres soluciones:

Una cuarta opción es satisfies, que comprueba el objeto contra un tipo conservando los tipos literales de sus propiedades.

as const

as const es una const assertion. Ponla después de una expresión y el compilador infiere el tipo más estrecho que puede:

  • los valores string, number y boolean conservan sus tipos literales
  • las propiedades de los objetos pasan a ser readonly
  • los arrays literales pasan a ser tuplas readonly de longitud fija

La aserción solo existe en tiempo de compilación. El JavaScript generado es el mismo objeto literal sin el as const, así que nada impide que otro código lo modifique en tiempo de ejecución. Si necesitas una garantía en tiempo de ejecución, llama también a Object.freeze.

Un tipo unión a partir de un array as const

Un patrón frecuente es guardar los valores permitidos en un array, que puedes recorrer en tiempo de ejecución, y derivar de él el tipo unión. (typeof arr)[number] significa «el tipo de cualquier elemento de arr».

Sin as const, ROLES sería string[] y Role sería simplemente string. El cast a readonly string[] en isRole hace falta porque includes sobre una tupla de literales solo acepta esos literales, y lo que quiere la función es comprobar un string que podría no ser uno de ellos. El mismo patrón con objetos funciona para mapas de clave y valor: const Status = { Active: "active", Banned: "banned" } as const, y después type Status = (typeof Status)[keyof typeof Status].

Parámetros de tipo const

Una función genérica normalmente ensancha los literales que le pasas. Desde TypeScript 5.0 puedes marcar un parámetro de tipo como const, lo que hace que el compilador infiera el argumento como si llevara as const, sin pedirle a quien llama que lo escriba.

Es sobre todo una herramienta para autores de librerías: las definiciones de rutas, los builders y las funciones auxiliares de esquemas lo usan para que quien llama obtenga tipos precisos a partir de literales normales.

Los significados de const

La palabra clave const aparece en cuatro sitios distintos del código TypeScript:

SintaxisTipoQué hace
const x = 1declaración de JavaScriptla variable no se puede reasignar; un valor literal conserva su tipo literal
expr as constaserción de TypeScriptel tipo más estrecho: literales, propiedades readonly, tuplas readonly
function f<const T>()parámetro de tipo de TypeScriptinfiere los argumentos como si llevaran as const
const enum E {}enum de TypeScriptun enum cuyos miembros se insertan en línea al compilar

Ninguno de ellos congela un objeto en tiempo de ejecución. const obj = { a: 1 } sigue permitiendo obj.a = 2; solo es un error reasignar obj en sí.

Preguntas frecuentes

¿Qué es un tipo literal en TypeScript?

Un tipo que admite exactamente un valor. "GET" es un tipo cuyo único valor es el string "GET", 404 es un tipo cuyo único valor es el número 404, y true es un tipo cuyo único valor es true. Son más útiles combinados en uniones, como type Method = "GET" | "POST".

¿Qué hace as const en TypeScript?

as const es una const assertion. Le dice al compilador que infiera el tipo más estrecho para una expresión: los valores string y number conservan sus tipos literales, las propiedades de los objetos pasan a ser readonly y los arrays literales pasan a ser tuplas readonly. Solo cambia el tipo; el valor en tiempo de ejecución es el mismo objeto o array normal, y no se congela.

¿Por qué TypeScript infiere string en lugar de mi literal?

Porque el valor es mutable. let x = "a" y la propiedad de { method: "GET" } se pueden reasignar más tarde, así que TypeScript los ensancha a string. Una variable const conserva el tipo literal "a". Para conservar los literales dentro de un objeto, anótalo con el tipo literal, usa as const o usa satisfies.

¿Qué diferencia hay entre const y as const?

const es una declaración de JavaScript: la variable no se puede reasignar, pero el objeto al que apunta sí se puede cambiar. as const es una aserción de tipo de TypeScript: hace que cada propiedad del valor sea readonly y literal en el sistema de tipos. Ninguno de los dos congela el objeto en tiempo de ejecución; para eso usa Object.freeze.

¿Cómo obtengo un tipo unión a partir de un array de strings?

Declara el array con as const y luego indexa su tipo con number: const roles = ["admin", "user"] as const; type Role = (typeof roles)[number]; da "admin" | "user". Sin as const el array es string[] y el resultado es solo string.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR