@yopadd/copy
v1.0.0
Published
A type-safe immutable object manipulation library with fluent API
Maintainers
Readme
Copy
A type-safe immutable object manipulation library with fluent API. Create, transform, and collect objects with full type inference.
Installation
npm install @yopadd/copyFeatures
- Immutable operations: All transformations create new objects, preserving the original
- Type-safe: Full TypeScript support with generic types that flow through all operations
- Fluent API: Chainable methods for clean, readable code
- Deep cloning: Uses
structuredClonefor deep copying of nested objects - Zero dependencies: Lightweight with no external dependencies
- Performance mode: Optimized for temporary objects with
fromMutable()
Usage
Basic Usage
import { Copy } from "@yopadd/copy";
// Create a copy from an object
const original = { name: "Alice", age: 30, email: "[email protected]" };
const copy = Copy.from(original);
// Get the cloned object
const result = copy.get();
// => { name: "Alice", age: 30, email: "[email protected]" }Omit Properties
Remove specified properties from the copy:
const result = Copy.from({ a: 1, b: 2, c: 3 }).omit(["a", "c"]).get();
// => { b: 2 }Keep Properties
Keep only specified properties:
const result = Copy.from({ a: 1, b: 2, c: 3 }).keep(["a", "c"]).get();
// => { a: 1, c: 3 }Add Properties
Add new properties to the copy:
const result = Copy.from({ a: 1 }).add("b", 2).get();
// => { a: 1, b: 2 }Add with Nullish Handling
Skip adding null or undefined values:
const copy = Copy.from({ a: 1 });
const result = copy
.add("b", null, { ignoreNullish: true })
.add("c", null)
.add("e", undefined, { ignoreNullish: true })
.add("f", undefined)
.get();
// => { a: 1, c: null, f: undefined } (b and e are not added)Map Properties
Transform property values:
const result = Copy.from({ a: 1, b: 2 })
.map("a", (x) => x * 10)
.get();
// => { a: 10, b: 2 }Collect to Class
Collect the final object into a class instance:
class User {
constructor(data: { name: string; age: number }) {
this.name = data.name;
this.age = data.age;
}
}
const user = Copy.from({ name: "Alice", age: 30 }).collectTo(User);
// => User { name: "Alice", age: 30 }Chaining Operations
All methods are chainable for fluent transformations:
const result = Copy.from({ a: 1, b: 2, c: 3, d: 4 })
.omit(["b"])
.add("e", 5)
.map("a", (x) => x + 10)
.keep(["a", "c", "e"])
.get();
// => { a: 11, c: 3, e: 5 }Performance Mode: fromMutable()
For temporary/disposable objects where you don't need to preserve the input:
// HTTP response that won't be reused
const response = await fetchUser();
// No initial clone, transformations mutate the response directly
const user = Copy.fromMutable(response)
.keep(["id", "name", "email"])
.add("createdAt", new Date().toISOString())
.collectTo(User);
// response is mutated, but we don't care since it's temporary⚠️ WARNING: When using fromMutable():
- The input object is NOT cloned initially
- All transformation methods will mutate the input object
- Use this only when the input is temporary and won't be reused
- For safe usage, always use
Copy.from()
API Reference
Copy.from<T>(struct: T): Copy<T>
Static method to create a new Copy instance from an object. Creates a deep clone immediately. Immutability is guaranteed.
Copy.fromMutable<T>(struct: T): Copy<T>
Static method to create a Copy instance without initial cloning. Optimized for temporary/disposable objects. The input will be mutated by transformation methods.
copy.omit<P>(props: P[]): Copy<T>
Returns a new Copy with the specified properties removed.
copy.keep<P>(props: P[]): Copy<T>
Returns a new Copy with only the specified properties.
copy.add<P, V>(prop: P, value: V, opts?: { ignoreNullish: boolean }): Copy<T>
Returns a new Copy with the specified property added.
- If
ignoreNullishistrue, null and undefined values are not added
copy.map<P, V>(prop: P, mapper: (value: T[P]) => V): Copy<T>
Returns a new Copy with the specified property transformed.
copy.collectTo<C>(constructor: { new (args: T): C }): C
Collects the current state into a class instance.
copy.get(): T
Returns the final cloned object.
Type Safety
The library provides strong TypeScript typing:
- Generic
Ttype flows through all operations omit()andkeep()narrow the type to exclude/include only specified propertiesadd()extends the type with the new propertymap()updates the type of the transformed property- All methods preserve type safety while allowing flexible transformations
Examples
Data Transformation Pipeline
interface UserData {
id: number;
name: string;
email: string;
password: string;
createdAt: string;
}
function sanitizeUser(user: UserData) {
return Copy.from(user).omit(["password"]).add("sanitized", true).get();
}
const user = {
id: 1,
name: "Alice",
email: "[email protected]",
password: "secret",
createdAt: "2024-01-01",
};
const safeUser = sanitizeUser(user);
// => { id: 1, name: "Alice", email: "[email protected]", createdAt: "2024-01-01", sanitized: true }Handling HTTP Response
import { Copy } from "@yopadd/copy";
interface ApiResponse {
data: any;
status: number;
headers: Record<string, string>;
}
async function getUser() {
const response = await fetch("/api/user");
const data: ApiResponse = await response.json();
// Optimized: no initial clone, we only need the data
return Copy.fromMutable(data)
.keep(["data"])
.collectTo(User);
}Partial Update with Type Safety
function updateUser<T extends { id: number }>(user: T, updates: Partial<T>) {
return Copy.from(user)
.add("updatedAt", new Date().toISOString())
.map("id", (id) => id) // Just an example, id stays the same
.get();
}Code written by @yopadd. Documentation generated by Mistral Vibe.
