TypeScript Objects as Dictionary types as in C#
ArraysObjectDictionaryTypescriptArrays Problem Overview
I have some JavaScript code that uses objects as dictionaries; for example a 'person' object will hold a some personal details keyed off the email address.
var people = {<email> : <'some personal data'>};
adding > "people[<email>] = <data>;"
getting > "var data = people[<email>];"
deleting > "delete people[<email>];"
Is it possible to describe this in Typescript? or do I have to use an Array?
Arrays Solutions
Solution 1 - Arrays
In newer versions of typescript you can use:
type Customers = Record<string, Customer>
In older versions you can use:
var map: { [email: string]: Customer; } = { };
map['[email protected]'] = new Customer(); // OK
map[14] = new Customer(); // Not OK, 14 is not a string
map['[email protected]'] = 'x'; // Not OK, 'x' is not a customer
You can also make an interface if you don't want to type that whole type annotation out every time:
interface StringToCustomerMap {
[email: string]: Customer;
}
var map: StringToCustomerMap = { };
// Equivalent to first line of above
Solution 2 - Arrays
In addition to using an map-like object, there has been an actual Map
object for some time now, which is available in TypeScript when compiling to ES6, or when using a polyfill with the ES6 type-definitions:
let people = new Map<string, Person>();
It supports the same functionality as Object
, and more, with a slightly different syntax:
// Adding an item (a key-value pair):
people.set("John", { firstName: "John", lastName: "Doe" });
// Checking for the presence of a key:
people.has("John"); // true
// Retrieving a value by a key:
people.get("John").lastName; // "Doe"
// Deleting an item by a key:
people.delete("John");
This alone has several advantages over using a map-like object, such as:
- Support for non-string based keys, e.g. numbers or objects, neither of which are supported by
Object
(no,Object
does not support numbers, it converts them to strings) - Less room for errors when not using
--noImplicitAny
, as aMap
always has a key type and a value type, whereas an object might not have an index-signature - The functionality of adding/removing items (key-value pairs) is optimized for the task, unlike creating properties on an
Object
Additionally, a Map
object provides a more powerful and elegant API for common tasks, most of which are not available through simple Object
s without hacking together helper functions (although some of these require a full ES6 iterator/iterable polyfill for ES5 targets or below):
// Iterate over Map entries:
people.forEach((person, key) => ...);
// Clear the Map:
people.clear();
// Get Map size:
people.size;
// Extract keys into array (in insertion order):
let keys = Array.from(people.keys());
// Extract values into array (in insertion order):
let values = Array.from(people.values());
Solution 3 - Arrays
You can use templated interfaces like this:
interface Map<T> {
[K: string]: T;
}
let dict: Map<number> = {};
dict["one"] = 1;
Solution 4 - Arrays
You can also use the Record type in typescript :
export interface nameInterface {
propName : Record<string, otherComplexInterface>
}
Solution 5 - Arrays
You can use Record
for this:
https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkt
Example (A mapping between AppointmentStatus enum and some meta data):
const iconMapping: Record<AppointmentStatus, Icon> = {
[AppointmentStatus.Failed]: { Name: 'calendar times', Color: 'red' },
[AppointmentStatus.Canceled]: { Name: 'calendar times outline', Color: 'red' },
[AppointmentStatus.Confirmed]: { Name: 'calendar check outline', Color: 'green' },
[AppointmentStatus.Requested]: { Name: 'calendar alternate outline', Color: 'orange' },
[AppointmentStatus.None]: { Name: 'calendar outline', Color: 'blue' }
}
Now with interface as value:
interface Icon { Name: string Color: string }
Usage:
const icon: SemanticIcon = iconMapping[appointment.Status]
Solution 6 - Arrays
Lodash has a simple Dictionary implementation and has good TypeScript support
Install Lodash:
npm install lodash @types/lodash --save
Import and usage:
import { Dictionary } from "lodash";
let properties : Dictionary<string> = {
"key": "value"
}
console.log(properties["key"])