Agent skill
single-or-array-pattern
Pattern for functions that accept either a single item or an array. Use when creating CRUD operations, batch processing APIs, or factory functions that should flexibly handle one or many inputs.
Stars
4,333
Forks
311
Install this agent skill to your Project
npx add-skill https://github.com/EpicenterHQ/epicenter/tree/main/.agents/skills/single-or-array-pattern
Metadata
Additional technical details for this skill
- author
- epicenter
- version
- 1.0
SKILL.md
Single-or-Array Pattern
Accept both single items and arrays, normalize at the top, process uniformly.
Quick Reference
typescript
function create(itemOrItems: T | T[]): Promise<Result<void, E>> {
const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
// ... implementation works on items array
}
The Structure
- Accept
T | T[]as the parameter type - Normalize with
Array.isArray()at the top of the function - All logic works against the array — one code path
typescript
function createServer(clientOrClients: Client | Client[], options?: Options) {
const clients = Array.isArray(clientOrClients)
? clientOrClients
: [clientOrClients];
// All real logic here, working with the array
for (const client of clients) {
// ...
}
}
Naming Conventions
| Parameter | Normalized Variable |
|---|---|
recordingOrRecordings |
recordings |
clientOrClients |
clients |
itemOrItems |
items |
paramsOrParamsArray |
paramsArray |
When to Use
Good fit:
- CRUD operations (create, update, delete)
- Batch processing APIs
- Factory functions accepting dependencies
- Any "do this to one or many" scenario
Skip when:
- Single vs batch have different semantics
- Return types vary significantly
- Array version needs different options
Codebase Examples
Server Factory (packages/server/src/server.ts)
typescript
function createServer(
clientOrClients: AnyWorkspaceClient | AnyWorkspaceClient[],
options?: ServerOptions,
) {
const clients = Array.isArray(clientOrClients)
? clientOrClients
: [clientOrClients];
// All server setup logic directly here
const workspaces: Record<string, AnyWorkspaceClient> = {};
for (const client of clients) {
workspaces[client.id] = client;
}
// ...
}
Database Service (apps/whispering/src/lib/services/isomorphic/db/web.ts)
typescript
delete: async (recordingOrRecordings) => {
const recordings = Array.isArray(recordingOrRecordings)
? recordingOrRecordings
: [recordingOrRecordings];
const ids = recordings.map((r) => r.id);
return tryAsync({
try: () => db.recordings.bulkDelete(ids),
catch: (error) => DbError.MutationFailed({ cause: error }),
});
},
Query Mutations (apps/whispering/src/lib/query/isomorphic/db.ts)
typescript
delete: defineMutation({
mutationFn: async (recordings: Recording | Recording[]) => {
const recordingsArray = Array.isArray(recordings)
? recordings
: [recordings];
for (const recording of recordingsArray) {
services.db.recordings.revokeAudioUrl(recording.id);
}
const { error } = await services.db.recordings.delete(recordingsArray);
if (error) return Err(error);
return Ok(undefined);
},
}),
Anti-Patterns
Don't: Separate functions for single vs array
typescript
// Harder to maintain, users must remember two APIs
function createRecording(recording: Recording): Promise<...>;
function createRecordings(recordings: Recording[]): Promise<...>;
Don't: Force arrays everywhere
typescript
// Awkward for single items
createRecordings([recording]); // Ugly
Don't: Duplicate logic in overloads
typescript
// BAD: Logic duplicated
function create(item: T) {
return db.insert(item); // Duplicated
}
function create(items: T[]) {
return db.bulkInsert(items); // Different code path
}
// GOOD: Single implementation
function create(itemOrItems: T | T[]) {
const items = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems];
return db.bulkInsert(items); // One code path
}
References
- Full article — detailed explanation with more examples
Didn't find tool you were looking for?