Menu

Tipi oggetto in TypeScript: proprietà opzionali e readonly

Come tipizzare gli oggetti in TypeScript: tipi oggetto inline, proprietà opzionali con ?, proprietà readonly, oggetti annidati, metodi, il controllo delle proprietà in eccesso e la differenza tra object, {} e Object.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Un tipo oggetto in TypeScript elenca le proprietà di un oggetto e il tipo di ciascuna: { name: string; age: number }. Aggiungi ? per rendere opzionale una proprietà e readonly per impedire che venga riassegnata. Scrivi il tipo inline, oppure dagli un nome con type o interface per riutilizzarlo.

Scrivere tipi oggetto

Le proprietà si separano con ; o , (funzionano entrambi, ; è lo stile abituale), e basta anche un semplice a capo. Un tipo inline va bene per un parametro usato una volta sola; per qualsiasi cosa usata due volte, dagli un nome.

// 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 descrivono le forme degli oggetti altrettanto bene. Le differenze (fusione delle dichiarazioni, unioni) sono spiegate nella pagina interface vs type.

Accedere a una proprietà che il tipo non dichiara è un errore di compilazione: point.z dà TS2339, Property 'z' does not exist on type '{ x: number; y: number; }'.

Proprietà opzionali

Un ? dopo il nome permette di omettere la proprietà. Leggere una proprietà opzionale dà T | undefined, quindi TypeScript ti obbliga a gestire il caso in cui manca prima di usarla.

Chiamare un metodo su una proprietà opzionale senza controllarla è un errore: p.nickname.toUpperCase() fallisce con TS18048, 'p.nickname' is possibly 'undefined'. Usa l'optional chaining (p.nickname?.toUpperCase()) quando undefined è un risultato accettabile.

prop?: T e prop: T | undefined non sono la stessa cosa. La prima permette che la chiave manchi; la seconda richiede la chiave, anche se il suo valore può essere undefined:

Proprietà readonly

readonly impedisce di riassegnare una proprietà dopo la creazione dell'oggetto. È solo un controllo in fase di compilazione, ed è superficiale: un oggetto o un array salvato in una proprietà readonly può ancora essere modificato al suo interno.

L'output mostra entrambi i limiti: l'id è cambiato davvero in fase di esecuzione (solo il compilatore sapeva che era readonly), e l'array interno è stato modificato. Per un array readonly usa readonly string[]; per rendere readonly tutte le proprietà in un colpo solo, usa Readonly<Order>.

Controllo delle proprietà in eccesso

Quando assegni un oggetto letterale direttamente a una variabile tipizzata o lo passi direttamente a un parametro tipizzato, TypeScript rifiuta ogni proprietà che il tipo non dichiara. Le proprietà in più in un letterale appena creato sono quasi sempre errori di battitura.

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'?

Il codice è TS2561 perché il compilatore ha trovato un nome simile; una proprietà in eccesso senza nomi simili dà TS2353, Object literal may only specify known properties, and 'z' does not exist in type 'Point'. Senza il controllo, l'errore di battitura compilerebbe, color sarebbe undefined e il programma disegnerebbe in nero senza avvisare. Il controllo vale solo per i letterali appena creati. Un oggetto che si trova già in una variabile può avere proprietà in più, perché la tipizzazione di TypeScript è strutturale: un valore è compatibile con un tipo quando ha almeno le proprietà obbligatorie.

Oggetti annidati e metodi

I tipi oggetto si possono annidare, e possono descrivere metodi sia con la sintassi dei metodi sia con una proprietà di tipo funzione.

Per forme profonde o riutilizzate, dai un nome al tipo interno (type Address = { ... }) e fai riferimento a quello, oppure estrailo dal tipo esterno con un accesso indicizzato, Company["address"], come fanno le ultime righe.

object vs {} vs Object

Tre tipi dal nome simile che significano cose diverse:

TipoAccettaRifiuta
objectqualsiasi non primitivo: {}, [], funzioni, istanze di classi5, "a", true, null, undefined
{}qualsiasi valore tranne null e undefined, primitivi compresinull, undefined
Objectlo stesso di {}, più un controllo che i membri integrati come toString mantengano tipi compatibilinull, undefined
{ x: number }qualsiasi valore con una x numericavalori senza x

{} non significa "un oggetto vuoto"; significa "né null né undefined". Per accettare qualsiasi oggetto con chiavi sconosciute, usa Record<string, unknown>; per una mappa da chiavi a valori, usa una firma di indice o Record, come mostrato nella pagina sui dizionari. Nella maggior parte dei casi, una forma specifica è meglio di tutti e tre.

Domande frequenti

Come si definisce un tipo oggetto in TypeScript?

Elenca le proprietà e i loro tipi tra parentesi graffe: { name: string; age: number }. Puoi scriverlo inline in un'annotazione, oppure dargli un nome con type User = { ... } o interface User { ... } e riutilizzarlo. Separa le proprietà con ; o ,.

Come si rende opzionale una proprietà in TypeScript?

Metti ? dopo il nome della proprietà: { name: string; nickname?: string }. L'oggetto può omettere nickname, e leggerla dà string | undefined, quindi devi controllarla o fornire un valore predefinito (user.nickname ?? user.name) prima di usarla come stringa.

Che differenza c'è tra prop?: string e prop: string | undefined?

Con prop?: string la chiave può mancare del tutto. Con prop: string | undefined la chiave è obbligatoria, anche se il suo valore può essere undefined, quindi {} è un errore di compilazione. Leggere l'una o l'altra dà string | undefined.

Che differenza c'è tra object, {} e Object in TypeScript?

object indica qualsiasi valore non primitivo (oggetti, array, funzioni) e rifiuta 5 o "a". {} indica qualsiasi valore tranne null e undefined, primitivi compresi. Object è quasi uguale a {}, ma controlla anche che i metodi integrati come toString mantengano tipi compatibili. Usa object, o meglio una forma specifica, invece di {} o Object.

Perché TypeScript dice che un oggetto letterale può specificare solo proprietà note?

È il controllo delle proprietà in eccesso: l'errore TS2353, o TS2561 quando il compilatore può suggerire la proprietà che probabilmente intendevi. Quando assegni un oggetto letterale appena creato direttamente a una variabile o a un parametro tipizzato, ogni proprietà che il tipo non dichiara viene segnalata, perché di solito è un errore di battitura. Assegnare un oggetto salvato in un'altra variabile salta il controllo, perché la tipizzazione strutturale ammette proprietà in più.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA