Menu

Tipos objeto en TypeScript: propiedades opcionales y readonly

Cómo tipar objetos en TypeScript: tipos objeto en línea, propiedades opcionales con ?, propiedades readonly, objetos anidados, métodos, la comprobación de propiedades de más y la diferencia entre object, {} y Object.

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

Un tipo objeto en TypeScript enumera las propiedades que tiene un objeto y el tipo de cada una: { name: string; age: number }. Añade ? para hacer opcional una propiedad y readonly para impedir que se reasigne. Escribe el tipo en línea o ponle nombre con type o interface para reutilizarlo.

Cómo escribir tipos objeto

Las propiedades se separan con ; o , (las dos formas funcionan, ; es el estilo habitual), y un salto de línea basta por sí solo. Un tipo en línea vale para un parámetro puntual; para cualquier cosa que uses dos veces, ponle nombre.

// Inline, in a parameter
function area(rect: { width: number; height: number }): number {
    return rect.width * rect.height;
}

// Named with a type alias
type Rect = { width: number; height: number };

// Named with an interface (the same shape)
interface RectShape {
    width: number;
    height: number;
}

type e interface describen formas de objeto igual de bien. Las diferencias (fusión de declaraciones, uniones) se explican en la página de interface frente a type.

Acceder a una propiedad que el tipo no declara es un error de compilación: point.z da TS2339, Property 'z' does not exist on type '{ x: number; y: number; }'.

Propiedades opcionales

Un ? después del nombre permite omitir la propiedad. Leer una propiedad opcional da T | undefined, así que TypeScript te obliga a manejar el caso en que falta antes de usarla.

Llamar a un método de una propiedad opcional sin comprobarla es un error: p.nickname.toUpperCase() falla con TS18048, 'p.nickname' is possibly 'undefined'. Usa el encadenamiento opcional (p.nickname?.toUpperCase()) cuando undefined sea un resultado aceptable.

prop?: T y prop: T | undefined no son lo mismo. La primera permite que la clave no exista; la segunda exige la clave, aunque su valor puede ser undefined:

Propiedades readonly

readonly impide reasignar una propiedad después de crear el objeto. Es solo una comprobación en tiempo de compilación, y es superficial: un objeto o array guardado en una propiedad readonly se puede seguir cambiando por dentro.

La salida muestra los dos límites: el id cambió de verdad en tiempo de ejecución (solo el compilador sabía que era readonly), y el array de dentro se modificó. Para un array readonly usa readonly string[]; para hacer readonly todas las propiedades a la vez, usa Readonly<Order>.

Comprobación de propiedades de más

Cuando asignas un objeto literal directamente a una variable tipada o lo pasas directamente a un parámetro tipado, TypeScript rechaza cualquier propiedad que el tipo no declare. Las propiedades de más en un literal recién creado son casi siempre erratas.

index.ts(8,8): error TS2561: Object literal may only specify known properties, but 'colour' does not exist in type 'Options'. Did you mean to write 'color'?

El código es TS2561 porque el compilador encontró un nombre parecido; una propiedad de más sin ningún nombre similar da TS2353, Object literal may only specify known properties, and 'z' does not exist in type 'Point'. Sin la comprobación, la errata compilaría, color sería undefined y el programa dibujaría en negro sin avisar. La comprobación solo se aplica a literales recién creados. Un objeto que ya está en una variable puede llevar propiedades de más, porque el tipado de TypeScript es estructural: un valor encaja en un tipo cuando tiene al menos las propiedades obligatorias.

Objetos anidados y métodos

Los tipos objeto se anidan, y pueden describir métodos tanto con sintaxis de método como con una propiedad de tipo función.

Para formas profundas o reutilizadas, pon nombre al tipo interior (type Address = { ... }) y haz referencia a él, o sácalo del tipo exterior con un acceso indexado, Company["address"], como hacen las últimas líneas.

object frente a {} y Object

Tres tipos que suenan parecido y significan cosas distintas:

TipoAceptaRechaza
objectcualquier no primitivo: {}, [], funciones, instancias de clases5, "a", true, null, undefined
{}cualquier valor excepto null y undefined, primitivos incluidosnull, undefined
Objectlo mismo que {}, más una comprobación de que los miembros integrados como toString mantienen tipos compatiblesnull, undefined
{ x: number }cualquier valor con una x numéricavalores sin x

{} no significa «un objeto vacío»; significa «ni null ni undefined». Para aceptar cualquier objeto con claves desconocidas, usa Record<string, unknown>; para un mapa de claves a valores, usa una firma de índice o Record, como se muestra en la página de diccionarios. La mayoría de las veces, una forma concreta es mejor que cualquiera de los tres.

Preguntas frecuentes

¿Cómo se define un tipo objeto en TypeScript?

Enumera las propiedades y sus tipos entre llaves: { name: string; age: number }. Puedes escribirlo en línea en una anotación o darle un nombre con type User = { ... } o interface User { ... } y reutilizarlo. Separa las propiedades con ; o ,.

¿Cómo se hace opcional una propiedad en TypeScript?

Pon ? después del nombre de la propiedad: { name: string; nickname?: string }. El objeto puede omitir nickname, y leerla da string | undefined, así que debes comprobarla o dar un valor por defecto (user.nickname ?? user.name) antes de usarla como string.

¿Qué diferencia hay entre prop?: string y prop: string | undefined?

Con prop?: string la clave se puede omitir por completo. Con prop: string | undefined la clave es obligatoria, aunque su valor puede ser undefined, así que {} es un error de compilación. Leer cualquiera de las dos da string | undefined.

¿Qué diferencia hay entre object, {} y Object en TypeScript?

object significa cualquier valor no primitivo (objetos, arrays, funciones) y rechaza 5 o "a". {} significa cualquier valor excepto null y undefined, primitivos incluidos. Object es casi lo mismo que {}, pero además comprueba que los métodos integrados como toString mantengan tipos compatibles. Usa object, o mejor una forma concreta, en lugar de {} u Object.

¿Por qué TypeScript se queja de que un objeto literal solo puede especificar propiedades conocidas?

Es la comprobación de propiedades de más: el error TS2353, o TS2561 cuando el compilador puede sugerir la propiedad que probablemente querías. Cuando asignas un objeto literal recién creado directamente a una variable o un parámetro tipado, se señala cualquier propiedad que el tipo no declare, porque suele ser una errata. Asignar un objeto guardado en otra variable se salta la comprobación, porque el tipado estructural permite propiedades de más.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR