20160913

Index Types in TypeScript

When working on dksfight, I needed to make some global maps to cache various things. Some of these maps were indexed by an integer, and others were indexed by strings. The values stored in the maps were various different types, doesn't matter. The keys presented a problem, though.

Here's a skeleton of the cache I tried to make. The reason for its existence is to tack on an access timestamp to the object, so I could know when to scavenge out entries to save memory.

class RecentCacheEntry<TValue>
{
    private _value : TValue;
    private _lastAccess : number;
    // rest of implementation omitted
}

type TIndex = string | number;
interface IKeyValueRecentCacheEntryMap<TValue>
{
    [index : TIndex] : RecentCacheEntry<TValue>;
}

class RecentCache<TValue>
{
    private map : IKeyValueRecentCacheEntryMap<TValue>;
    // rest of implementation omitted
}

let someCache : RecentCache<number> = new RecentCache<number>();

At face value this seems pretty intuitive. I have a map type that is templated with the value and allows a string or number to index it. It would be nice to template using the index type, too, but that adds even more hassles, and I'm willing to forego that level of type safety here.

So what's wrong with this code? Well, compiler errors:

error TS1023: An index signature parameter type must be 'string' or 'number'.

Uhhhh, can't you read? The type is string or number. It's literally string | number. It turns out there is confusion here, and all indexes in JavaScript are always strings, so the error is fairly misleading. The fix? Always use a string (sigh).

interface IKeyValueRecentCacheEntryMap<TValue>
{
    [index : string] : RecentCacheEntry<TValue>;
}

Goodbye, TIndex. I'm gonna miss you, buddy.

0 comments:

Post a Comment