如何从打字稿中的类型中排除 getter 只有属性
How to exclude getter only properties from type in typescript
class 中的 getter 是只读属性,因此从以下代码中抛出类型错误是有意义的。
class Car {
engine: number;
get hp() {
return this.engine / 2;
}
get kw() {
return this.engine * 2;
}
}
function applySnapshot(
car: Car,
snapshoot: Partial<Car> // <-- how to exclude readonly properties?
) {
for (const key in snapshoot) {
if (!snapshoot.hasOwnProperty(key)) continue;
car[key as keyof Car] = snapshoot[key as keyof Car];
// Cannot assign to 'hp' because it is a constant or a read-only property.
}
}
有没有办法将只写属性转换为类型并排除所有 getter?
编辑
有关此问题的有趣解决方法,请参阅@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
这是一个已知问题,记录在案 here。
由于此可分配性规则,无法在条件类型中区分只读字段和可变字段之间的区别。
一种解决方法是向只读字段类型添加一些额外的内容。这不会影响您使用该字段的方式,但它会给我们一个钩子来删除密钥。
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
})
虽然readonly
不直接影响类型是否可赋值,但会影响它们是否相同。为了测试两种类型是否相同,我们可以滥用(1)条件类型的可分配性规则,这要求 extends
之后的类型相同,或者(2)交集类型的推理过程,抛出双方的类型相同。然后我们只使用 Titian Cernicova-Dragomir 的回答中的映射类型依次查看 Car
的每个 属性 并查看它是否与自身的可变版本相同。
// 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];
}
}
嘿,我的问题可能有你的答案。
基本上你可以通过这样做排除所有的吸气剂(和函数)
class Car {
engine: number = 1;
get hp() {
return this.engine / 2;
}
get kw() {
return this.engine * 2;
}
}
var snapShot = {...new Car()};
type CarNoGetters = typeof snapShot;
那么你的函数会像这样工作:
function applySnapshot(
car: Car,
snapshoot: CarNoGetters
) {
for (const key of Object.keys(snapshoot) as Array<keyof typeof snapshoot>) {
car[key] = snapshoot[key];
}
}
我的问题询问如何在不使用 Javascript 的情况下获取类型 CarNoGetters
,即。 var snapShot = {...new Car()};
但如果您不在乎,可以使用它。
(注意我用的是 TS ^3.75)
class 中的 getter 是只读属性,因此从以下代码中抛出类型错误是有意义的。
class Car {
engine: number;
get hp() {
return this.engine / 2;
}
get kw() {
return this.engine * 2;
}
}
function applySnapshot(
car: Car,
snapshoot: Partial<Car> // <-- how to exclude readonly properties?
) {
for (const key in snapshoot) {
if (!snapshoot.hasOwnProperty(key)) continue;
car[key as keyof Car] = snapshoot[key as keyof Car];
// Cannot assign to 'hp' because it is a constant or a read-only property.
}
}
有没有办法将只写属性转换为类型并排除所有 getter?
编辑 有关此问题的有趣解决方法,请参阅@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
这是一个已知问题,记录在案 here。
由于此可分配性规则,无法在条件类型中区分只读字段和可变字段之间的区别。
一种解决方法是向只读字段类型添加一些额外的内容。这不会影响您使用该字段的方式,但它会给我们一个钩子来删除密钥。
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
})
虽然readonly
不直接影响类型是否可赋值,但会影响它们是否相同。为了测试两种类型是否相同,我们可以滥用(1)条件类型的可分配性规则,这要求 extends
之后的类型相同,或者(2)交集类型的推理过程,抛出双方的类型相同。然后我们只使用 Titian Cernicova-Dragomir 的回答中的映射类型依次查看 Car
的每个 属性 并查看它是否与自身的可变版本相同。
// 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];
}
}
嘿,我的问题可能有你的答案。
基本上你可以通过这样做排除所有的吸气剂(和函数)
class Car {
engine: number = 1;
get hp() {
return this.engine / 2;
}
get kw() {
return this.engine * 2;
}
}
var snapShot = {...new Car()};
type CarNoGetters = typeof snapShot;
那么你的函数会像这样工作:
function applySnapshot(
car: Car,
snapshoot: CarNoGetters
) {
for (const key of Object.keys(snapshoot) as Array<keyof typeof snapshoot>) {
car[key] = snapshoot[key];
}
}
我的问题询问如何在不使用 Javascript 的情况下获取类型 CarNoGetters
,即。 var snapShot = {...new Car()};
但如果您不在乎,可以使用它。
(注意我用的是 TS ^3.75)