TypeScript
TypeScript copied to clipboard
2.4: Readonly<Map<k,v>> vs. ReadonlyMap<k,v>
TypeScript Version: 2.4.1
Code
function GenMap() : ReadonlyMap<string, number> {
const theMap = new Map<string, number>();
theMap.set("one", 1);
theMap.set("two", 2);
return Object.freeze(theMap);
}
Expected behavior: Compiles without error in tsc@<2.4.0
Actual behavior:
TryX.ts(5,5): error TS2322: Type 'Readonly<Map<string, number>>' is not assignable to type 'ReadonlyMap<s
tring, number>'.
Property '[Symbol.iterator]' is missing in type 'Readonly<Map<string, number>>'.
You cannot make a Map read only with Object.freeze. Sounds like this is working as intended to me.
That's surprising. @sylvanaar can you provide a reference supporting this?
No matter what the runtime-behavior, Its is a change in compile-time behavior from TypeScript 2.3.x.,
https://stackoverflow.com/questions/35747325/is-there-a-way-to-freeze-a-es6-map
Also, you can test it yourself: http://jsbin.com/zorumaceyu/edit?js,console
I think they gave a reasonable type for a Map returned from Object.freeze in the current release Readonly<Map<T>>
#12377 they added support for ReadOnlyArray, buit shouldn't have.
We need new overloads to freeze,:
freeze<K,V>(a: Map<K,V>): ReadonlyMap<K,V>;
and set as well since we are at it:
freeze<T>(a: Set<T>): ReadonlySet<T>;
PRs welcomed
@mhegazy , as @sylvanaar 's comment/link shows, calling Object.freeze on a map doesn't actually stop new keys being added with .set(). Do the suggested overloads still make sense in this case?
Readonly<Map<_, _>>
is not the same as ReadonlyMap<_, _>
. I think the latter is a subset of the former, so the best solution would be to make Readonly<Map<_, _>>
assignable to ReadonlyMap<_, _>
.
This issue is caused by the fact that keyof
does not include well known symbols (in this case Symbol.iterator
). This problem has been already reported in issue #24622 and will be resolved with #24738.
Interestingly ReadonlyArray<T>
and Readonly<Array<T>
are assignable to each other. I wish that Readonly<Foo>
would alias to ReadonlyFoo
as long as it's in scope. Ideally, we should also check that Foo
is assignable to ReadonlyFoo
.