如何在方法中干净地传递 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
