DEV Community

Kinga
Kinga

Posted on • Updated on

Type-safe Dictionary in TypeScript

Although Map object has almost everything I need, I recently found myself in need of map() function on a Mapobject... No such luck. ;)
So I implemented it.

export interface IDictionary<T> {
    set(key: string, value: T);
    get(key: string): T;
    delete(key: string): boolean;
    hasKey(key: string): boolean;
    hasValue(value: T): boolean;
    getKeys(): string[];
    getValues(): T[];
    map(fn: (key: string, value: T) => any ): {};

    size: number;
}
export class Dictionary<T> implements IDictionary<T>{ 
    private items: { [key: string]: T } = {};

    public set(key: string, value: T) {
        this.items[key] = value;
    }
    public get(key: string): T {
        return this.items[key];
    }
    public delete(key: string):boolean { 
        if (this.items.hasOwnProperty(key)) { 
            delete this.items[key];
            return true;
        }
        else { 
            return false;
        }
    }

    public hasKey(key: string): boolean {
        return this.items.hasOwnProperty(key);
    }
    public hasValue(value:T): boolean { 
        return Object.values(this.items).includes(value);
    }

    public getKeys(): string[] {
        return Object.keys(this.items);
    }
    public getValues(): T[] {
        return Object.values(this.items);
    }

    public map(mapFn: { (key: string, value:T): any; }): {} { 
        return Object.keys(this.items).map((key) => { 
            return mapFn(key,this.items[key]);
        });

    }
    public format(mapFn: { (key: string, value:T): any; }): any[] { 
        return Object.keys(this.items).map((key) => { 
            return mapFn(key,this.items[key]);
        });
    }

    public get size(): number { 
        return Object.keys(this.items).length;
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, printing Dictionary in a React function couldn't be easier

export default function CommitForm(props: ICommitFormProps) {
    let myDictionary: Dictionary<Array<string>> = (new Dictionary());
...
    return (
        <ul>
            {
                myDictionary.map((key: string, value: string[]) => {
                    return (<li>{value.join(", ")} (type {key}) </li>);
                })
            }
        </ul>
    );
...
}
Enter fullscreen mode Exit fullscreen mode

And in case you want to use the Dictionary in your localized string:

myStrings.d.ts
  Warning_HTML: string;
Enter fullscreen mode Exit fullscreen mode
en-us.js
  "Warning_HTML":"The following values are not supported:<ul>{0}</ul>"
Enter fullscreen mode Exit fullscreen mode
MyReactComponent.tsx
export default function CommitForm(props: ICommitFormProps) {
    let myDictionary: Dictionary<Array<string>> = (new Dictionary());
...

    function getHTML(fields: Dictionary<string[]>): string { 
        return (fields.format((key, value) => { return "<li>" + value.join(", ") + " (type " + key + ") </li>"; })).join();
    }

    return (
    <MessageBar messageBarType={MessageBarType.warning} isMultiline={true} >
    {
        <div dangerouslySetInnerHTML={{ __html: format(strings.Warning_HTML, getHTML(myDictionary)) }} ></div>
    }
    </MessageBar>
    );
...
}
Enter fullscreen mode Exit fullscreen mode

The difference between Object, Mapand Record is widely documented (see e.g. Building a type-safe dictionary in TypeScript for an explanation).

Top comments (0)