typescript - 以类型安全的方式迭代对象属性

标签 typescript typescript-typings

我有一个不可变的类设置,其中包含大量成员,并且希望提供一种简单的方法来创建修改后的副本。我从类似的事情开始

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/

相关文章:

javascript - 没有硬编码字符串的 Jasmine spy

json - 在 typescript 中获取两个对象数组之间不匹配的对象

TypeScript 返回相同的嵌套对象结构但类型不同

javascript - 包含将发布到 npm 的 js 包的 Typescript 类型的正确方法是什么?

TypeScript:如何创建一个类型或接口(interface),它是一个具有 n 个键之一或者是字符串的对象?

javascript - Webpack 找不到 Typescript 类型解析所需的导入。如何解决这个问题?

javascript - Typescript 数组推送,console.log 的奇怪行为

node.js - Karma 测试在 chrome 中失败,但在 phantomjs 中通过

javascript - 有什么方法可以将 Node 项目转换为 Deno?

javascript - typescript 将字符串文字类型映射为大写