Here is the current method to achieve that:
type Mutable<T extends { [x: string]: any }, K extends string> = {
[P in K]: T[P];
}
declare function clone<T>(val: T): Mutable<T, keyof T>;
(Check out the code in playground)
This approach is further discussed in this Mapped Types syntax for removing modifiers article.
Original answer:
It seems there is no direct way to remove the readonly
restriction, for instance:
declare function clone<T>(val: T): {[K in keyof T]: T[K]};
const rw = clone(ro);
rw.foo = 'changed';
This will still result in the readonly error.
However, it is possible to do the opposite by starting with a "writeable" interface and then restricting it to readonly:
interface Data {
foo: string;
}
const ro: Readonly<Data> = {
foo: 'bar'
}
declare function clone<T>(val: Readonly<T>): T;
const rw = clone(ro);
rw.foo = 'changed';
Note that I have changed Data
from a class to an interface since it is not being used as a class in your example.
If you intend to use it as a class, you would need to instantiate it:
const ro = new Data();
ro.foo = "bar";
Or
const ro = Object.assign(new Data(), { foo: "bar" });
The previous edit involved using a bug unintentionally:
I stand corrected, you can indeed bypass the readonly modifier:
declare function clone<T>(val: T): {[K in (keyof T)]: T[K]};
const rw = clone(ro);
rw.foo = 'changed';
This works seamlessly, with the key difference being (keyof T)
instead of just keyof T
. There is an ongoing issue related to this on: Mapped Type bug involving modifiers, which has been acknowledged as a bug.