Using ReadonlyMap type

TypeScript defines ReadonlyMap<K, V> interface which is immutable version of standard JavaScript Map<K, V> type.

How are we supposed to use ReadonlyMap<K, V> in our code?

It’s not derived from anything and doesn’t declare constructor. So, is it true that we just should do:

public publicApiMethod(): ReadonlyMap<K, V> {
    const map: Map<K, V> = new Map(...);
    return map as ReadonlyMap<K, V>;
}

Or there are a better approaches to using ReadonlyMap<K, V> rather than just typecasting?

Short answer: you’re doing it right.

ReadonlyMap<K, V> is essentially a supertype of Map<K, V> since the methods and properties it does have match up with those of Map<K,V>. So by returning a Map as a ReadonlyMap, all you’re doing is widening the type of the value. By the way, that means you can skip the type assertion:

public publicApiMethod(): ReadonlyMap<K, V> {
    const map: Map<K, V> = new Map(...);
    return map; // no assertion necessary
}

Just like you could do this:

public anotherMethod(): string | number {
    return "hey"; // always "hey", but assignable to string | number
}

The benefit is that a caller is not free to assume that the returned value has any of the other Map methods, and trying to set or clear the map contents will produce a compile time error, despite the fact that those methods would indeed exist at runtime. (Or for the anotherMethod() example, the caller cannot know that the return value is always a string and can’t directly call any string-specific methods on it.)

This compile-time prohibition of runtime behavior is similar to the way the readonly modifier works. When a property is readonly, TypeScript will complain if you try to modify it, even though the modification would succeed at runtime.

Read More:   Show datalist labels but submit the actual value

You could, if you wanted, implement your own version of ReadonlyMap that is read-only even at runtime, but that’s only worth the effort if you have a use case that requires it. If you don’t, (and you probably don’t) then don’t worry about it and keep doing it the way you’re doing it.

Hope that helps; good luck!

To be safe in runtime, we may represent ReadonlyMap like that:

const attrs: Map<string, string> = new Map();
...

const trueRuntimeReadonlyMap: ReadonlyMap<string, string> = Object.freeze({
  entries: attrs.entries.bind(attrs),
  forEach: attrs.forEach.bind(attrs),
  get: attrs.get.bind(attrs),
  has: attrs.has.bind(attrs),
  keys: attrs.keys.bind(attrs),
  size: attrs.size,
  values: attrs.values.bind(attrs),
  [Symbol.iterator]: attrs[Symbol.iterator].bind(attrs)
});


The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Similar Posts