Closed
Description
TypeScript Version: 2.4.2
Code
function mapAssign<T, K extends keyof T>(obj: T, k: K, f: (v: T[K]) => T[K]) {
obj[k] = f(obj[k]);
}
type NoChange = {
readonly value: number
}
const obj: NoChange = {value: 5};
mapAssign(obj, "value", x => x+1);
Expected behavior:
Since NoChange.value
is readonly
, it should not be modifiable.
Actual behavior:
The code compiles and runs, modifying obj.value
.
This might just be a design limitation that can't be worked around.
If a stricter check was desirable, I could imagine it being something like this:
- if
K extends keyof T
andk: K
andx: T
, thenx[k] = e
would fail, butT = {readonly [k]: any}
would be okay - if
K extends !readonly keyof T
andk: K
andx: T
thenx[k] = e
would succeed, butT = {readonly [k]: any}
would fail
So the above program would become
function mapAssign<T, K extends !readonly keyof T>(obj: T, k: K, f: (v: T[K]) => T[K]) {
obj[k] = f(obj[k]); // okay, since K extends !readonly keyof T
}
mapAssign(obj, "value", x => x+1); // error: Const.value is readonly
An alternative change (which I think is much less ergonomic) would be the following:
- if
K extends keyof T
andk: K
andx: T
thenx[k] = e
would succeed, butT = {readonly [k]: any}
would fail - if
K extends readonly keyof T
andk: K
andx: T
thenx[k] = e
would fail butT = {readonly [k]: any}
would succeed.
So the program would be
function mapAssign<T, K extends keyof T>(obj: T, k: K, f: (v: T[K]) => T[K]) {
obj[k] = f(obj[k]);
}
mapAssign(obj, "value", x => x+1); // error: Const.value is readonly