long - ¿Imponer el tipo de los miembros indexados de un objeto Typescript?
typescript undefined (5)
Basándose en la respuesta de @shabunc, esto permitiría aplicar la clave o el valor, o ambos, para que sea lo que quiera aplicar.
type IdentifierKeys = ''my.valid.key.1'' | ''my.valid.key.2'';
type IdentifierValues = ''my.valid.value.1'' | ''my.valid.value.2'';
let stuff = new Map<IdentifierKeys, IdentifierValues>();
También debería funcionar usando enum
lugar de una definición de type
.
Me gustaría almacenar un mapeo de cadena -> cadena en un objeto de TypeScript, y hacer que todas las claves se correlacionen con las cadenas. Por ejemplo:
var stuff = {};
stuff["a"] = "foo"; // okay
stuff["b"] = "bar"; // okay
stuff["c"] = false; // ERROR! bool != string
¿Hay alguna forma de que haga cumplir que los valores deben ser cadenas (o el tipo que sea ...)?
La respuesta de @Ryan Cavanaugh está totalmente bien y sigue siendo válida. Todavía vale la pena agregar que a partir del otoño de 2016, cuando podemos afirmar que ES6 es compatible con la mayoría de las plataformas, casi siempre es mejor adherirse a Map cada vez que necesite asociar algunos datos con alguna clave.
Cuando escribimos let a: { [s: string]: string; }
let a: { [s: string]: string; }
tenemos que recordar que después de que se compile el texto mecanografiado no hay datos de tipo, solo se usa para compilar. Y {[s: cadena]: cadena; } se compilará solo a {}.
Dicho eso, incluso si escribes algo así como:
class TrickyKey {}
let dict: {[key:TrickyKey]: string} = {}
Esto simplemente no compilará (incluso para el target es6
, obtendrá el error TS1023: An index signature parameter type must be ''string'' or ''number''.
Prácticamente está limitado con la cadena o el número como clave potencial, por lo que no tiene sentido imponer la verificación de tipo aquí, especialmente teniendo en cuenta que cuando js intenta acceder a la clave por número, la convierte en cadena.
Por lo tanto, es bastante seguro asumir que la mejor práctica es usar Map incluso si las claves son de cadena, así que me quedaría con:
let staff: Map<string, string> = new Map();
Llevo este archivo conmigo a donde sea que vaya
export interface IStringTMap<T> { [key: string]: T; };
export interface INumberTMap<T> { [key: number]: T; };
export interface IStringAnyMap extends IStringTMap<any> {};
export interface INumberAnyMap extends INumberTMap<any> {};
export interface IStringStringMap extends IStringTMap<string> {};
export interface INumberStringMap extends INumberTMap<string> {};
export interface IStringNumberMap extends IStringTMap<number> {};
export interface INumberNumberMap extends INumberTMap<number> {};
export interface IStringBooleanMap extends IStringTMap<boolean> {};
export interface INumberBooleanMap extends INumberTMap<boolean> {};
IStringTMap
e INumberTMap
son genéricos y se pueden usar para crear mapas de cualquier tipo (a través de let myTypeMap: IStringTMap<MyType> = {}
). El resto son mapeos predefinidos útiles entre tipos literales comunes.
La sintaxis importante aquí es { [key: string]: T; }
{ [key: string]: T; }
que denota que la interfaz impone un literal de objeto con claves de tipo string
(la palabra key
puede ser cualquier identificador, y debe usarse para indicar la importancia de la clave) y valores de tipo T
Si desea construir un objeto con "nombres" de string
para claves y valores boolean
(y no usar la herencia anterior), su interfaz sería { [name: string]: boolean }
.
Una actualización rápida: desde Typescript 2.1 hay un tipo incorporado Record<T, K>
que actúa como un diccionario.
Un ejemplo de documentos:
// For every properties K of type T, transform it to U
function mapObject<K extends string, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>
const names = { foo: "hello", bar: "world", baz: "bye" };
const lengths = mapObject(names, s => s.length); // { foo: number, bar: number, baz: number }
Documentación de TypeScript 2.1 en el Record<T, K>
La única desventaja que veo al usar esto sobre {[key: T]:K
es que puedes codificar información útil sobre qué tipo de clave estás usando en lugar de "clave", por ejemplo, si tu objeto solo tenía claves principales que podrías indicar. eso me gusta: {[prime: number]: yourType}
.
Aquí hay una expresión regular que escribí para ayudar con estas conversiones. Esto solo convertirá los casos donde la etiqueta es "clave". Para convertir otras etiquetas simplemente cambie el primer grupo de captura:
Buscar: /{/s*/[(key)/s*(+/s*:/s*(/w+)/s*/]/s*:/s*([^/}]+?)/s*;?/s*/}
Reemplazar: Record<$2, $3>
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