Редактировать См. @ Matt-mccutchen для интересного решения этой проблемы.
Оригинальный ответ
readonly
довольно слабыйМодификатор в этом смысле не влияет на присваиваемость.Например, вы можете назначить объект со свойствами readonly
объекту с такими же изменяемыми свойствами, и компилятор не будет жаловаться:
let roCar: Partial<Car> = { hp: 10 } // we can assign a mutable object to a referecne with a readonly property
roCar.hp = 10; // error hp is readonly
//But we can also assign an object with a readonly property to a fully mutable version of it
let allMutableCar: { -readonly [P in keyof Car]: Car[P] } = new Car();
allMutableCar.hp = 10; // No compile time error
Это известная проблема, задокументированная здесь ,
Из-за этого правила присваивания в условных типах невозможно различить разницу между полем только для чтения и изменяемым.
Один из способов - добавить что-то дополнительное к типу полей только для чтения.,Это не повлияет на то, как вы можете использовать поле, но даст нам возможность удалить ключ.
type readonly = { readonly?: undefined };
class Car {
engine!: number;
get hp() : number & readonly {
return this.engine / 2;
}
get kw() : number & readonly {
return this.engine * 2;
}
}
type NoReadonlyKeys<T> = { [P in keyof T]: 'readonly' extends keyof T[P] ? never : P }[keyof T]
type PartialNoReadonly<T> = Partial<Pick<T, NoReadonlyKeys<T>>>
type Mutable<T> = { -readonly [P in keyof T]: T[P] }
function applySnapshot(
car: Car,
snapshoot: PartialNoReadonly<Car>
) {
const mutableCar: Mutable<Car> = car; // erase readonly so we can mutate
for (const key in snapshoot) {
let typedKey = key as keyof typeof snapshoot
if (!snapshoot.hasOwnProperty(key)) continue;
mutableCar[typedKey] = snapshoot[typedKey] as any;
}
}
applySnapshot(new Car(), {
engine: 0
})
applySnapshot(new Car(), {
hp: 0 /// error
})