用于比较(和更新)Typescript 中对象的某些字段的类型定义

Type definition for comparing (and updating) certain fields of objects in Typescript

我想比较和分配两个对象的某些字段,比方说:

const left = { a: 1, ignored: 5, something: 7 };
const right = { a: 2, ignored: 6, else: 8, id: 18 };

我想调用 leftToRight(left, right, ['a']) 之后 right 应该是:

{ a: 1, ignored: 6, id: 18 }

我想调用其他函数并传递 right.id,我知道它确实存在于第二个参数中。

我目前的做法是:

leftToRight(left, right, keys) {
  let changed = false;
  
  for (const key of keys) {
    if (!Object.is(left[key], right[key])) {
      right[key] = left[key];
      changed = true;
    }
  }

  doSomething(right.id)

  return changed
}

我正在努力寻找合适的类型定义:-(

初始方法:

leftToRight<T>(left: T, right: T, keys: Array<keyof T>): boolean

导致:“属性 'id' 在类型 'T' 上不存在”,我没有办法检查它 ('id' in right)

第二次尝试:

leftToRight<T>(left: T, right: T & {id: number}, keys: Array<keyof T>): boolean

导致 right[key] = left[key]

的“类型 'T' 不可分配给类型 '{ id: number; }'”

第三次尝试:

leftToRight<T, U extends {id: number}>(left: T, right: U, keys: Array<keyof T & keyof U>): boolean

再次导致赋值错误 right[key] = left[key] 因为类型 T 和 U 可能完全不相关。

尝试使用 left: Partial<T>,并断言 left[key] 不是未定义的 !

Example:

const left = { a: 1, ignored: 5};
const right = { a: 2, ignored: 6, id: 18 };

function leftToRight<T>(left: Partial<T>, right: T, keys: Array<keyof T>) {
  let changed = false;
  
  for (const key of keys) {
    if (!Object.is(left[key], right[key])) {
      right[key] = left[key]!;
      changed = true;
    }
  }

  // doSomething(right.id)

  return changed
}

编辑:

您已阐明您的要求,即 LeftRight 可能各自具有彼此不存在的属性。为了处理这种情况,我们需要两个泛型类型。

Right 是一个包含 id 的对象。我们希望所选键同时出现在 LeftRight 中,并且我们希望它们具有相同的值类型。 我将泛型 Keys 定义为 Right 的键的任何子集。我将 Left 定义为包含所有这些键的 Right 的子集。

function leftToRight<Right extends {id: number}, Keys extends keyof Right>(
  left: Pick<Right, Keys>, right: Right, keys: Keys[]
) {
  let changed = false;
  
  for (const key of keys) {
    if (!Object.is(left[key], right[key])) {
      right[key] = left[key];
      changed = true;
    }
  }

  doSomething(right.id)

  return changed
}

Playground Link

原答案:

在找到一个可行的方法之前,我尝试了一些方法。当然,这不是最干净的定义。我在使用泛型描述 Left 并添加 id 以获得 Right 时出错,但从 Right 中删除有效。

我们说 Right 是一个 {id: number} 属性 的对象, Left 必须具有 Right 除了 [=15= 之外的所有属性].为了让 keys 的元素出现在两个对象上,我们需要忽略 keyof Right.

中的键 id
function leftToRight<Right extends {id: number}>(
  left: Omit<Right, 'id'>, right: Right, keys: (Exclude<keyof Right, 'id'>)[]
) {
  let changed = false;
  
  for (const key of keys) {
    if (!Object.is(left[key], right[key])) {
      right[key] = left[key];
      changed = true;
    }
  }

  doSomething(right.id)

  return changed
}

Playground Link