Menu

Indexed access types in TypeScript: T[K], T[number] e altro

Un indexed access type legge il tipo di una proprietà da un altro tipo: Person["age"] è number. Impara T["key"], le chiavi unione, T[keyof T], T[number] per gli elementi di un array, gli indici delle tuple e (typeof arr)[number] per trasformare un array const in un'unione.

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

Un indexed access type cerca il tipo di una proprietà in un altro tipo, con la stessa sintassi a parentesi quadre che JavaScript usa per accedere alle proprietà. Person["age"] è number, e Person["address"]["city"] è string.

Il vantaggio è un'unica fonte di verità. Age non è una seconda copia di number: se Person["age"] diventa string, ogni tipo scritto come Person["age"] cambia con lui. Gli indexed access types esistono solo in fase di compilazione; non producono JavaScript.

Chiavi unione e T[keyof T]

L'indice può essere un'unione di chiavi. Il risultato è l'unione dei tipi delle proprietà:

Product[keyof Product] è il modo, a livello di tipi, per dire "qualsiasi valore di proprietà di Product". Chiedere una chiave che non esiste è un errore di compilazione: Product["price"] dà error TS2339: Property 'price' does not exist on type 'Product'.

Le proprietà facoltative includono undefined

Con strictNullChecks (attivo con strict), il tipo di una proprietà facoltativa include undefined, e lo stesso vale per il suo indexed access type:

type Profile = { name: string; nickname?: string };

type Nick = Profile["nickname"]; // string | undefined

Per togliere undefined, avvolgilo: NonNullable<Profile["nickname"]> è string.

Tipi degli elementi di un array con T[number]

Gli array si indicizzano con numeri, quindi indicizzare un tipo array con number dà il tipo degli elementi. Funziona anche su più livelli:

È utile quando un tipo arriva da qualcosa che non controlli, come un client API generato: ApiResponse["data"]["users"][number] dà un nome al tipo utente senza che nessuno l'abbia esportato.

Tuple: indicizzare per posizione

Un tipo tupla si può indicizzare con una posizione specifica, con number per l'unione di tutti i tipi degli elementi, e con "length" per la sua lunghezza come tipo letterale:

(typeof arr)[number]: un'unione da un array const

L'uso più comune nel mondo reale: tieni un elenco di valori ammessi come array (così puoi ciclarci sopra a runtime) e ricavane il tipo unione (così il compilatore lo controlla).

Due elementi lo fanno funzionare. as const rende l'array una tupla readonly di tipi letterali; senza, ROLES è string[] e (typeof ROLES)[number] è solo string. Poi typeof trasforma il valore in un tipo, e [number] raccoglie i suoi elementi in un'unione. Le parentesi sono facoltative (typeof ROLES[number] significa la stessa cosa), ma rendono chiaro l'ordine delle operazioni.

Controllare una stringa rispetto all'array

Quella tupla readonly riserva una sorpresa. Il suo metodo includes accetta solo il tipo degli elementi, quindi non puoi passargli una stringa qualsiasi:

index.ts(6,25): error TS2345: Argument of type 'string' is not assignable to parameter of type '"admin" | "editor" | "viewer"'.

Allarga l'array a readonly string[] per il controllo, e trasforma la funzione in una type guard così un controllo riuscito restringe il valore:

Questo schema ti dà un solo elenco per entrambi i compiti: il controllo a runtime usa l'array e il tipo ne viene ricavato, quindi non possono mai divergere.

Indexed access nei generics: T[K]

Dentro un generic, T[K] con K extends keyof T è il tipo della proprietà scelta da chi chiama:

function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
  return items.map((item) => item[key]);
}

const users = [
  { name: "Ada", age: 36 },
  { name: "Linus", age: 28 },
];

const names = pluck(users, "name"); // string[]
const ages = pluck(users, "age");   // number[]

È il vincolo K extends keyof T a rendere lecito T[K]. Senza, TypeScript non può sapere che K è una chiave di T e segnala error TS2536: Type 'K' cannot be used to index type 'T'.

La sintassi in sintesi

ScrittoSignificaRisultato di esempio
T["key"]tipo di una proprietàPerson["age"] è number
T["a" | "b"]unione dei tipi di quelle proprietàstring | number
T[keyof T]unione di tutti i tipi delle proprietàogni tipo di valore
T["a"]["b"]proprietà annidataPerson["address"]["city"]
Arr[number]tipo degli elementi di un arrayUser[] dà User
Tup[0]elemento della tupla in una posizionetipo del primo elemento
Tup["length"]lunghezza della tupla come letterale3
(typeof arr)[number]unione dei valori di un array const"admin" | "editor"
T[typeof key]indice con il tipo di una costanteuguale a T["name"]

L'indice è sempre un tipo. Person[key], dove key è una variabile const, fallisce con due errori: error TS2538: Type 'key' cannot be used as an index type. e error TS2749: 'key' refers to a value, but is being used as a type here. Did you mean 'typeof key'?.

Domande frequenti

Cos'è un indexed access type in TypeScript?

Un tipo scritto T[K] che cerca il tipo della proprietà K nel tipo T. Con type Person = { name: string; age: number }, Person["age"] è number. Usa la stessa sintassi a parentesi quadre dell'accesso alle proprietà in JavaScript, ma lavora sui tipi in fase di compilazione.

Come si ottiene il tipo degli elementi di un array in TypeScript?

Indicizza il tipo array con number: per type Users = User[], Users[number] è User. Per un valore array, combinalo con typeof: (typeof users)[number].

Come si trasforma un array di stringhe in un tipo unione?

Dichiara l'array con as const così TypeScript conserva i valori letterali, poi indicizzalo con number: const roles = ["admin", "editor"] as const; type Role = (typeof roles)[number]; dà "admin" | "editor". Senza as const il tipo degli elementi è solo string.

Perché non posso usare una variabile come indice in un indexed access type?

L'indice deve essere un tipo. Person[key] con const key = "name" fallisce (errori TS2538 e TS2749) perché key è un valore. Scrivi Person[typeof key], oppure usa direttamente il letterale: Person["name"].

Cosa significa T[keyof T]?

Indicizza T con l'unione di tutte le sue chiavi, quindi il risultato è l'unione di tutti i tipi delle sue proprietà. Per { a: string; b: number } è string | number. È il modo, a livello di tipi, per dire "qualsiasi valore di questo oggetto".

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA