Imporre il tipo di membri indicizzati di un object Typescript?

Vorrei memorizzare una mapping di string -> stringa in un object Typescript e imporre che tutte le chiavi siano mappate su stringhe. Per esempio:

var stuff = {}; stuff["a"] = "foo"; // okay stuff["b"] = "bar"; // okay stuff["c"] = false; // ERROR! bool != string 

C’è un modo per farmi valere che i valori devono essere stringhe (o qualunque tipo ..)?

 var stuff: { [s: string]: string; } = {}; stuff['a'] = ''; // ok stuff['a'] = 4; // error // ... or, if you're using this a lot and don't want to type so much ... interface StringMap { [s: string]: string; } var stuff2: StringMap = { }; // same as above 

Porto con me questo file con me ovunque vada

 export interface IStringTMap { [key: string]: T; }; export interface INumberTMap { [key: number]: T; }; export interface IStringAnyMap extends IStringTMap {}; export interface INumberAnyMap extends INumberTMap {}; export interface IStringStringMap extends IStringTMap {}; export interface INumberStringMap extends INumberTMap {}; export interface IStringNumberMap extends IStringTMap {}; export interface INumberNumberMap extends INumberTMap {}; export interface IStringBooleanMap extends IStringTMap {}; export interface INumberBooleanMap extends INumberTMap {}; 

IStringTMap e INumberTMap sono generici e possono essere utilizzati per creare mappe di qualsiasi tipo (tramite let myTypeMap: IStringTMap = {} ). Il resto sono utili mappature predefinite tra tipi letterali comuni.

La syntax importante qui è { [key: string]: T; } { [key: string]: T; } che denota che l’interfaccia impone un object letterale con le chiavi di tipo string (la parola key può essere qualsiasi identificatore, e dovrebbe essere usato per indicare l’importanza della chiave) e valori di tipo T Se si voleva build un object con “nomi” di string per chiavi e valori boolean (e non usare l’ereditarietà sopra) l’interfaccia sarebbe { [name: string]: boolean } .

@Ryan La risposta di Cavanaugh è totalmente ok e comunque valida. Vale comunque la pena di aggiungere che a partire da Fall’16, quando possiamo affermare che ES6 è supportato dalla maggior parte delle piattaforms, è quasi sempre meglio attenersi alla mappa ogni volta che è necessario associare alcuni dati con qualche chiave.

Quando scriviamo, let a: { [s: string]: string; } let a: { [s: string]: string; } dobbiamo ricordare che dopo che il typescript è stato compilato non c’è qualcosa come i dati di tipo, è usato solo per la compilazione. E {[s: string]: string; } verrà compilato a solo {}.

Detto questo, anche se scriverai qualcosa come:

 class TrickyKey {} let dict: {[key:TrickyKey]: string} = {} 

Questo semplicemente non verrà compilato (anche per il target es6 , riceverai l’ error TS1023: An index signature parameter type must be 'string' or 'number'.

Quindi in pratica sei limitato a stringhe o numeri come potenziale chiave, quindi non c’è molto senso applicare il controllo dei tipi qui, soprattutto tenendo presente che quando js tenta di accedere alla chiave per numero lo converte in stringa.

Quindi è abbastanza sicuro supporre che la migliore pratica è usare Map anche se le chiavi sono string, quindi rimango con:

 let staff: Map = new Map(); 

Basandosi sulla risposta di @ shabunc, ciò consentirebbe di imporre la chiave o il valore – o entrambi – a tutto ciò che si vuole applicare.

 type IdentifierKeys = 'my.valid.key.1' | 'my.valid.key.2'; type IdentifierValues = 'my.valid.value.1' | 'my.valid.value.2'; let stuff = new Map(); 

Dovrebbe anche funzionare usando enum posto di una definizione di type .

Un rapido aggiornamento: dal Typescript 2.1 c’è un tipo di Record incorporato Record che si comporta come un dizionario.

Un esempio di documenti:

 // For every properties K of type T, transform it to U function mapObject(obj: Record, f: (x: T) => U): Record const names = { foo: "hello", bar: "world", baz: "bye" }; const lengths = mapObject(names, s => s.length); // { foo: number, bar: number, baz: number } 

Documentazione TypeScript 2.1 su Record

L’unico svantaggio che vedo usando questo {[key: T]:K è che puoi codificare informazioni utili su quale tipo di chiave stai usando al posto di “key”, ad esempio se il tuo object aveva solo le prime key a cui potresti suggerire così: {[prime: number]: yourType} .

Ecco una regex che ho scritto per aiutare con queste conversioni. Questo convertirà solo i casi in cui l’etichetta è “chiave”. Per convertire altre etichette, modifica semplicemente il primo gruppo di acquisizione:

Trova: \{\s*\[(key)\s*(+\s*:\s*(\w+)\s*\]\s*:\s*([^\}]+?)\s*;?\s*\}

Sostituisci: Record<$2, $3>