logo hsb.horse
← Back to snippets index

Snippets

Property Extractor Helper Function

Generate a function that extracts only specified properties from an object. Consolidate type-safe property extraction into one line for nested mapBy and array transformations.

Published: Updated:

When transforming arrays with map, it’s common to need only specific properties rather than the entire object. Writing this inline can be verbose, but defining a property extractor function upfront enables concise expressions like map(propsExtractor('id', 'name')).

Code

type LiteralObject = Record<string, unknown>
/**
* Generate a function that extracts only specified properties from an object
*/
export function propsExtractor<
Props extends LiteralObject,
PropKey extends keyof Props,
>(
...props: PropKey[]
): (obj: Props) => Pick<Props, PropKey> {
return (obj: Props) => {
const result: Partial<Props> = {}
for (const prop of props) {
if (Object.hasOwn(obj, prop)) {
result[prop] = obj[prop]
}
}
return result as Pick<Props, PropKey>
}
}

Usage

type User = {
id: string
name: string
email: string
role: string
createdAt: Date
}
const users: User[] = [
{ id: '1', name: 'Alice', email: 'alice@example.com', role: 'admin', createdAt: new Date() },
{ id: '2', name: 'Bob', email: 'bob@example.com', role: 'user', createdAt: new Date() },
]
// Extract only ID and name
const userSummaries = users.map(propsExtractor('id', 'name'))
// => [{ id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }]
// Works cleanly even in nested maps
const departments = [
{ name: 'Engineering', members: users },
{ name: 'Sales', members: users },
]
const summaries = departments.map(dept => ({
department: dept.name,
members: dept.members.map(propsExtractor('id', 'name'))
}))

How It Works

  1. propsExtractor accepts property names to extract as variadic arguments
  2. Returns a function of type (obj: Props) => Pick<Props, PropKey> as a closure
  3. The returned function checks property existence with Object.hasOwn before extracting
  4. TypeScript’s Pick utility type ensures accurate type inference for the return value

Using Object.hasOwn ensures safety by not mistakenly extracting properties from the prototype chain.

Benefits

  • Readable: Intent is clear with expressions like map(propsExtractor('id', 'name'))
  • Type-safe: TypeScript type inference accurately determines the extracted type
  • Reusable: Define once, use in multiple locations
  • Handles nesting well: Maintains readability even with multiple mapBy applications

Caveats

Properties that don’t exist are not included in the result (not even as undefined). If you want to include all properties, remove the Object.hasOwn check. Also, if extracting many properties, explicitly defining an interface may be more maintainable.

Applications

  • Extract only necessary fields from API responses before returning to clients
  • Exclude properties containing sensitive information during logging (create an inverse filter version)
  • Extract partial state in Redux or Zustand selectors
  • Implement partial retrieval logic similar to GraphQL field selection