How to exclude getter only properties from type in typescript

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];
    }
}

Leave a Comment