Using ReadonlyMap<K, V> type
Asked Answered
C

2

32

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?

Concavity answered 26/4, 2018 at 15:15 Comment(0)
S
33

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.

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.

Substantiate answered 26/4, 2018 at 15:42 Comment(0)
B
0

To feel safe yourself at compile time, we may use like that:

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

To feel safe yourself at runtime, we may use like that:

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

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

    return trueRuntimeReadonlyMap;
}
Busey answered 23/9, 2022 at 12:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.