在JavaScript/TypeScript中移除对象属性且保持原对象完整的实现方案
The Problem
I have a TypeScript Product object with nested arrays, and I need to send it to an API but clear out the priceHistory and purchaseHistory properties (both on the top-level Product and each item in the parts array). Crucially, I don't want to modify the original object.
Here's my original Product structure:
const originalProduct = { productId: 1, productName: "ABC", orders: [{ orderId: 1, orderDate: "2020-01-01", amount: 100 }, { orderId: 2, orderDate: "2020-01-01", amount: 200 }], priceHistory: [{ date: "2020-01-1", price: 15 }, { date: "2020-02-1", price: 16 }], purchaseHistory: [{ purchaseDate: "2019-01-01", purchaseAmt: 10, qty: 20 }, { purchaseDate: "2019-02-01", purchaseAmt: 10, qty: 2 }], parts: [{ partId: 1, partName: "Part 1", priceHistory: [], orders: [], purchaseHistory: [] }, { partId: 2, partName: "Part 2", priceHistory: [], orders: [], purchaseHistory: [] }] };
I want the resulting object to keep all other properties intact, but set priceHistory and purchaseHistory to empty arrays (both on the parent and nested parts).
My first attempt modified the original object because I was just assigning a reference:
// ❌ This modifies the original object! let newProduct = originalProduct; newProduct.priceHistory = []; newProduct.purchaseHistory = [] newProduct.parts.forEach((part) => { part.priceHistory = []; part.purchaseHistory = []; });
The Solution
The core issue with your initial code is that newProduct = originalProduct just copies a reference to the same object in memory. To avoid modifying the original, you need to create a deep enough copy of the object, then adjust the properties you want to clear.
Option 1: Manual Copy (Simple, for Known Structure)
If your object structure is fixed and not deeply nested beyond parts, you can use the spread operator (...) to create a shallow copy of the top-level object, then map over the parts array to create copies of each part:
// ✅ Creates a new object without modifying the original const newProduct = { // Copy all top-level properties from the original ...originalProduct, // Override the properties we want to clear priceHistory: [], purchaseHistory: [], // Map over parts to create new copies of each part (clear their target properties) parts: originalProduct.parts.map(part => ({ ...part, priceHistory: [], purchaseHistory: [] })) };
This works because:
- The spread operator
...originalProductcreates a new top-level object (not a reference). parts.map(...)returns a new array with new part objects (so we don't mutate the originalpartsarray or its items).
Option 2: Reusable Function (For Dynamic/Deeply Nested Structures)
If you anticipate more nested properties or want a reusable solution, write a recursive function that clears specified properties across all nested objects:
/** * Creates a copy of an object (and nested objects/arrays) and clears specified properties to empty arrays * @param obj - The original object to copy * @param propsToClear - Array of property names to clear * @returns A new object with target properties cleared */ function cleanNestedObject<T extends object>(obj: T, propsToClear: string[]): T { // Create a shallow copy of the current object const cleanedObj = { ...obj }; // Clear the target properties by setting them to empty arrays propsToClear.forEach(prop => { if (cleanedObj.hasOwnProperty(prop)) { cleanedObj[prop as keyof T] = [] as any; } }); // Recursively process any nested arrays of objects for (const key in cleanedObj) { const value = cleanedObj[key]; if (Array.isArray(value)) { cleanedObj[key] = value.map(item => typeof item === 'object' && item !== null ? cleanNestedObject(item, propsToClear) : item ) as typeof value; } } return cleanedObj; } // Usage const newProduct = cleanNestedObject(originalProduct, ['priceHistory', 'purchaseHistory']);
This function handles:
- Top-level properties
- Nested arrays of objects (like
parts) - Any deeper nesting you might add later (e.g., if parts had their own nested objects with
priceHistory)
Why Your Initial Code Failed
When you do let newProduct = originalProduct, you're not creating a new object—you're just pointing newProduct to the same object in memory as originalProduct. Any changes to newProduct (including modifying nested properties like parts) will directly affect the original object. Using the spread operator or a dedicated copy function breaks this reference link.
内容的提问来源于stack exchange,提问作者developer

