网站搜索

如何在方法中干净地传递 TypeScript 对象的字段子集?


我经常发现自己需要在 TypeScript 中传递一个大对象的字段子集。我通常会使用解构来稍微整理一下,但我并不喜欢即使代码没有重复,看起来也很重复。我是不是错过了更好的方法?

interface MyLargerObject {
  field1: string;
  field2: string;
  field3: string;
  field4: string;
  field5: string;
  field6: string;
  field7: string;
  field8: string;
  field9: string;
  otherField1: string;
  otherField2: string;
  otherField3: string;
  otherField4: string;
  otherField5: string;
}

interface MySmallerObject {
  field1: string;
  field2: string;
  field3: string;
  field4: string;
  field5: string;
  otherField1: string;
  otherField2: string;
}

function subsetMethod(largerObject: MyLargerObject): MySmallerObject {
  const {
    field1,
    field2,
    field3,
    field4,
    field5
  } = largerObject;
  
  return {
    field1,
    field2,
    field3,
    field4,
    field5,
    otherField1: 'new1',
    otherField2: 123
  };
}

另外,我们假设 MyLargerObject 和 MySmallerObject 之间可能会有重叠。在这种情况下,我希望确保不会用 largerObject 的值覆盖 MySmallerObject 的重叠字段。

答案#1

如果你打算经常编写这种代码,你可以像这样创建一个辅助函数:

function pick<T extends object, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K> {
  const ret = {} as Pick<T, K>;
  for (const key of keys) { ret[key] = obj[key] }
  return ret;
}

pick() 函数接受一个泛型类型为 T 的对象 obj,以及一个键的列表 keys,该列表元素的联合类型是泛型 K。实现过程中会将所有指定的属性复制到一个新对象中,该对象随后被返回,并被赋予类型 Pick<T, K>。

请注意,我在实现中需要一个类型断言,const ret={} as Pick<T, K>,这并不是类型安全的(显然一个空对象可能不是该类型),但计划是在函数返回时该断言将是正确的。


使用 pick(),你可以在 largerObject 上调用它,并传入你关心的键,然后将其展开到你返回的对象字面量中,这样你就可以添加任何你需要的其他属性:

function subsetMethod(largerObject: MyLargerObject): MySmallerObject {
  return {
    ...pick(largerObject, "field1", "field2", "field3", "field4", "field5"),
    otherField1: "abc",
    otherField2: "def"
  }
}

代码的 Playground 链接

答案#2

另一种方法是直接在返回对象中展开整个 largerObject。

前提是你的项目是完全类型化的,并且你不介意额外的内存使用,因为它会深拷贝 largerObject 的字段。

interface MyLargerObject {
  field1: string;
  field2: string;
  otherField1: string;
}

interface MySmallerObject {
  field1: string;
  otherField1: string;
}

function subsetMethod(largerObject: MyLargerObject): MySmallerObject {
   return {
     ...largerObject,
     otherField1: 'new1'
   };
}

const obj = subsetMethod({ field1: '1', field2: '2', otherField1: '3'})

obj.field1 // ok
obj.field2 // error