While readonly
does not directly affect whether types are assignable, it does affect whether they are identical. To test whether two types are identical, we can abuse either (1) the assignability rule for conditional types, which requires that the types after extends
be identical, or (2) the inference process for intersection types, which throws out identical types from both sides. Then we just use mapped types as in Titian Cernicova-Dragomir's answer to look at each property of Car
in turn and see whether it is identical to a mutable version of itself.
// https://github.com/Microsoft/TypeScript/issues/27024#issuecomment-421529650
type IfEquals<X, Y, A, B> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? A : B;
// Alternatively:
/*
type IfEquals<X, Y, A, B> =
[2] & [0, 1, X] extends [2] & [0, 1, Y] & [0, infer W, unknown]
? W extends 1 ? B : A
: B;
*/
type WritableKeysOf<T> = {
[P in keyof T]: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P, never>
}[keyof T];
type WritablePart<T> = Pick<T, WritableKeysOf<T>>;
class Car {
engine: number;
get hp() {
return this.engine / 2;
}
get kw() {
return this.engine * 2;
}
}
function applySnapshot(
car: Car,
snapshoot: Partial<WritablePart<Car>>
) {
let key: keyof typeof snapshoot;
for (key in snapshoot) {
if (!snapshoot.hasOwnProperty(key)) continue;
car[key] = snapshoot[key];
}
}
T
)and identical (including theirreadonly
attributes), while this works it does seem a bit inconsistent, do we know (ie have some assurance form the compiler team) this will not break in future compiler releases. – Esdras