我有一个不可变的类设置
,其中包含大量成员,并且希望提供一种简单的方法来创建修改后的副本。我从类似的事情开始
class Settings {
private constructor(public readonly a: number,
public readonly b: number) {
}
static newSettings() {
return new Settings(1, 2);
}
withA(a: number) {
return new Settings(a, this.b);
}
withB(a: number) {
return new Settings(this.a, b);
}
}
这正是我在 Java 中的做法(使用 Lombok 生成所有样板文件)。这不能很好地扩展,所以我切换到
interface ISettings {
readonly a?: number
readonly b?: number
}
class Settings implements ISettings {
readonly a: number = 1
readonly b: number = 2
private constructor(base?: ISettings, overrides?: ISettings) {
for (const k of Object.keys(this)) {
// @ts-ignore
this[k] = overrides?.[k] ?? base?.[k] ?? this[k]; // <---- PROBLEM
}
}
static newSettings() {
return new Settings();
}
with(overrides: ISettings) {
return new Settings(this, overrides);
}
}
它可以工作,但我必须在一行中使用@ts-ignore
,恕我直言,这应该是开箱即用的。
我知道,我可以使用例如immerjs
(提供这个和更多),但是我怎样才能得到正确的打字?
最佳答案
问题的根源
首先,由于 TS 结构类型,Object.keys
将始终返回字符串类型
The basic rule for TypeScript’s structural type system is that x is compatible with y if y has at least the same members as x
一般来说,这意味着TS允许传递子类型,换句话说,可以传递具有更多成员/字段的值,这意味着Object.keys
将拥有比我们想象的更多的键。
此类行为的示例:
type X = { a: string };
type Y = X & { b: string };
function f(x: X) {
return Object.keys(x);
}
const y = { a: 'a', b: 'b' };
f(y); // no error - works but we have more keys then X has
解决方法
我会采用这种方法:
private constructor(base?: ISettings, overrides?: ISettings) {
const keys = Object.keys(this) as (keyof ISettings)[]
for (const k of keys) {
// k is a | b
this[k] = overrides?.[k] ?? base?.[k] ?? this[k];
}
}
由于 Object.keys
的行为,这里唯一的方法是通过 as
进行类型断言。
关于typescript - 以类型安全的方式迭代对象属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61066060/