Obj
Object utilities for working with plain JavaScript objects.
Provides type-safe utilities for object operations including property access, manipulation, merging, filtering, and transformations. Supports both value-level and type-level operations with strong type inference.
Import
import { Obj } from '@wollybeard/kit'
import * as Obj from '@wollybeard/kit/obj'
Namespaces
Union
- Union operations on objects.
This module provides utilities for working with unions of object types, solving common TypeScript limitations when dealing with union types:
keyof (A | B)
returns only common keys (intersection), not all keys (union) -(A | B)['key']
returnsany
for keys not in all members - No built-in way to merge union members while preserving value unions per key
These utilities use distributive conditional types to properly handle each union member separately, then combine the results.
Access
[F]
getWith
<pathInput extends PropertyPathInput>(pathInput: pathInput): <obj extends InferShapeFromPropertyPath<normalizePropertyPathInput<pathInput>>>(obj: obj) => getWith<normalizePropertyPathInput<pathInput>, obj>
Parameters:
pathInput
- A dot-notation string or array of property names
Returns: A function that extracts the value at the specified path
Create a getter function for a specific property path. Returns a function that extracts the value at that path from any compatible object.
Examples:
const getCityName = Obj.getWith('address.city')
getCityName({ address: { city: 'NYC' } }) // 'NYC'
getCityName({ address: { city: 'LA' } }) // 'LA'
// Type-safe property access
const getAge = Obj.getWith(['user', 'profile', 'age'])
const data = { user: { profile: { age: 30 } } }
const age = getAge(data) // number
// Useful for mapping over arrays
const users = [
{ name: 'Alice', score: 95 },
{ name: 'Bob', score: 87 },
]
users.map(Obj.getWith('score')) // [95, 87]
[F]
getOn
(obj: object): (pathInput: PropertyPathInput) => unknown
Parameters:
obj
- The object to extract values from
Returns: A function that accepts a property path and returns the value at that path
Create a getter function bound to a specific object. Returns a function that can extract values from that object using any property path. Inverse parameter order of getWith.
Examples:
const user = {
name: 'Alice',
address: { city: 'NYC', zip: '10001' },
}
const getUserProp = Obj.getOn(user)
getUserProp('name') // 'Alice'
getUserProp('address.city') // 'NYC'
getUserProp(['address', 'zip']) // '10001'
// Useful for extracting multiple properties
const config = { api: { url: 'https://api.com', key: 'secret' } }
const getConfig = Obj.getOn(config)
const apiUrl = getConfig('api.url')
const apiKey = getConfig('api.key')
[F]
entries
<obj extends Any>(obj: obj): { [K in keyof obj] -?: undefined extends obj[K] ? {} extends Pick<obj, K> ? [K, Exclude<obj[K]>] : [K, obj[K]] : [K, obj[K]]; } [keyof obj][]
Parameters:
obj
- The object to extract entries from
Returns: An array of tuples containing [key, value] pairs
Get an array of key-value pairs from an object. Preserves exact types including optional properties and undefined values.
Examples:
Obj.entries({ a: 1, b: 'hello', c: true })
// Returns: [['a', 1], ['b', 'hello'], ['c', true]]
// Handles optional properties and undefined values
Obj.entries({ a: 1, b?: 2, c: undefined })
// Returns proper types preserving optionality
[F]
stringKeyEntries
<$T extends object>(obj: $T): [string & keyof $T, $T[keyof $T]][]
Parameters:
obj
- The object to extract entries from
Returns: An array of [key, value] tuples where keys are strings
Get entries from an object with string keys only.
Examples:
const obj = { a: 1, b: 2 }
Obj.stringKeyEntries(obj) // [['a', 1], ['b', 2]]
[F]
entriesStrict
<$T extends object>(obj: $T): { [k in keyof $T]: [k, Exclude<$T[k], undefined>]; } [keyof $T][]
Parameters:
obj
- The object to extract entries from
Returns: An array of [key, value] tuples excluding undefined values
Get entries from an object excluding undefined values.
Examples:
const obj = { a: 1, b: undefined, c: 2 }
Obj.entriesStrict(obj) // [['a', 1], ['c', 2]]
[F]
keysStrict
<$T extends object>(obj: $T): (keyof $T)[]
Parameters:
obj
- The object to extract keys from
Returns: An array of keys
Get keys from an object with proper type inference. Type-safe version of Object.keys.
Examples:
const obj = { a: 1, b: 2 }
Obj.keysStrict(obj) // ['a', 'b'] with type ('a' | 'b')[]
[F]
getRandomly
<obj extends Any>(obj: obj): keyof obj extends never ? undefined : obj[keyof obj]
Parameters:
obj
- The object to get a random value from
Returns: A random value from the object, or undefined for empty objects
Get a random property value from an object
Examples:
Obj.getRandomly({ a: 1, b: 2, c: 3 }) // Could return 1, 2, or 3
Obj.getRandomly({ a: 1, b: undefined }) // Could return 1 or undefined
Obj.getRandomly({}) // Returns undefined
[F]
getValueAtPath
<$T, ___Path extends readonly string[] > (obj: $T, path: ___Path): any
Parameters:
obj
- The object to traversepath
- Array of property names representing the path
Returns: The value at the path, or undefined if not found
Get a value at a path in an object.
Examples:
const obj = { a: { b: { c: 42 } } }
Obj.getValueAtPath(obj, ['a', 'b', 'c']) // 42
Obj.getValueAtPath(obj, ['a', 'x']) // undefined
[F]
values
<$T extends object>(obj: $T): values<$T>
Parameters:
obj
- The object to extract values from
Returns: An array of values
Get an array of values from an object. Type-safe version of Object.values.
Examples:
const obj = { a: 1, b: 'hello', c: true }
Obj.values(obj) // [1, 'hello', true] with type (string | number | boolean)[]
Filtering
[F]
pick
<T extends object, K extends keyof T > (obj: T, keys: readonly K[]): Pick < T, K >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value?: undefined, obj?: undefined) => boolean): Partial < $Object >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value: $Object[keyof $Object], obj?: $Object | undefined) => boolean): Partial < $Object >
<T extends object, K extends keyof T > (obj: T, keysOrPredicate: readonly K[] | ((key: keyof T, value?: any, obj?: any) => boolean)): Pick<T, K> | Partial<T>
Parameters:
obj
- The object to pick properties from
Returns: A new object containing only the specified properties
Create a new object with only the specified properties.
Examples:
// Using array of keys
const user = { name: 'Alice', age: 30, email: 'alice@example.com' }
const publicInfo = Obj.pick(user, ['name', 'email'])
// Result: { name: 'Alice', email: 'alice@example.com' }
// Type-safe property selection
interface User {
id: number
name: string
password: string
email: string
}
function getPublicUser(user: User) {
return Obj.pick(user, ['id', 'name', 'email'])
// Type: Pick<User, 'id' | 'name' | 'email'>
}
// Using a predicate function (key only)
const obj = { a: 1, b: 2, c: 3 }
Obj.pick(obj, k => k !== 'b') // { a: 1, c: 3 }
// Using a filter function (key, value, obj)
const obj = { a: 1, b: 2, c: 3 }
Obj.pick(obj, (k, v) => v > 1) // { b: 2, c: 3 }
Obj.pick(obj, (k, v, o) => v > average(o)) // picks above-average values
[F]
omit
<T extends object, K extends keyof T > (obj: T, keys: readonly K[]): Omit < T, K >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value?: undefined, obj?: undefined) => boolean): Partial < $Object >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value: $Object[keyof $Object], obj?: $Object | undefined) => boolean): Partial < $Object >
<T extends object, K extends keyof T > (obj: T, keysOrPredicate: readonly K[] | ((key: keyof T, value?: any, obj?: any) => boolean)): Omit<T, K> | Partial<T>
Parameters:
obj
- The object to omit properties from
Returns: A new object without the specified properties
Create a new object with the specified properties removed.
Examples:
// Using array of keys
const user = { name: 'Alice', age: 30, password: 'secret' }
const safeUser = Obj.omit(user, ['password'])
// Result: { name: 'Alice', age: 30 }
// Remove sensitive fields
interface User {
id: number
name: string
password: string
apiKey: string
}
function sanitizeUser(user: User) {
return Obj.omit(user, ['password', 'apiKey'])
// Type: Omit<User, 'password' | 'apiKey'>
}
// Using a predicate function (key only)
const obj = { a: 1, b: 2, c: 3 }
Obj.omit(obj, k => k === 'b') // { a: 1, c: 3 } (excludes b)
// Using a filter function (key, value, obj)
const obj = { a: 1, b: 2, c: 3 }
Obj.omit(obj, (k, v) => v > 1) // { a: 1 } (excludes b and c where value > 1)
Obj.omit(obj, (k, v, o) => v > average(o)) // excludes above-average values
[F]
pickWith
<K extends PropertyKey>(keysOrPredicate: readonly K[] | ((key: PropertyKey, value?: any, obj?: any) => boolean)): <T extends object>(obj: T) => any
Curried version of pick
- takes keys/predicate first, then object.
[F]
pickOn
<T extends object>(obj: T): <K extends keyof T>(keysOrPredicate: readonly K[] | ((key: keyof T, value?: any, obj?: any) => boolean)) => any
Curried version of pick
- takes object first, then keys/predicate.
[F]
omitWith
<K extends PropertyKey>(keysOrPredicate: readonly K[] | ((key: PropertyKey, value?: any, obj?: any) => boolean)): <T extends object>(obj: T) => any
Curried version of omit
- takes keys/predicate first, then object.
[F]
omitOn
<T extends object>(obj: T): <K extends keyof T>(keysOrPredicate: readonly K[] | ((key: keyof T, value?: any, obj?: any) => boolean)) => any
Curried version of omit
- takes object first, then keys/predicate.
[F]
policyFilter
<$Object extends object, $Key extends Keyof<$Object>, $Mode extends 'allow' | 'deny' > (mode: $Mode, obj: $Object, keys: readonly $Key[]): PolicyFilter<$Object, $Key, $Mode>
Parameters:
mode
- 'allow' to keep only specified keys, 'deny' to remove specified keysobj
- The object to filterkeys
- The keys to process
Returns: A filtered object with proper type inference
Filter object properties based on a policy mode and set of keys.
Examples:
const obj = { a: 1, b: 2, c: 3 }
// Allow mode: keep only 'a' and 'c'
Obj.policyFilter('allow', obj, ['a', 'c']) // { a: 1, c: 3 }
// Deny mode: remove 'a' and 'c'
Obj.policyFilter('deny', obj, ['a', 'c']) // { b: 2 }
[C]
omitUndefined
;(<T>(obj: T) => any)
Remove all properties with undefined
values from an object.
Examples:
const obj = { a: 1, b: undefined, c: 'hello', d: undefined }
Obj.omitUndefined(obj) // { a: 1, c: 'hello' }
// Useful for cleaning up optional parameters
const config = {
host: 'localhost',
port: options.port, // might be undefined
timeout: options.timeout, // might be undefined
}
const cleanConfig = Obj.omitUndefined(config)
// Only includes properties that have actual values
[C]
partition
partition
Partition an object into picked and omitted parts.
Examples:
const obj = { a: 1, b: 2, c: 3 }
const { picked, omitted } = Obj.partition(obj, ['a', 'c'])
// picked: { a: 1, c: 3 }
// omitted: { b: 2 }
[F]
pickMatching
<T extends object>(obj: T, predicate: (key: string) => boolean): Partial<T>
Parameters:
obj
- The object to filterpredicate
- Function that returns true to keep a key
Returns: A new object with only the key/value pairs where key predicate returned true
Filter object properties by key pattern matching. Useful for extracting properties that match a pattern like data attributes.
Examples:
const props = {
'data-type': 'button',
'data-current': true,
onClick: fn,
className: 'btn',
}
const dataAttrs = Obj.pickMatching(props, key => key.startsWith('data-'))
// Result: { 'data-type': 'button', 'data-current': true }
Merging
[F]
mergeWith
(mergers?: MergeOptions | undefined): <obj1 extends Any, obj2 extends Any>(obj1: obj1, obj2: obj2) => obj1 & obj2
Parameters:
mergers
- Options to customize merge behavior
Returns: A merge function with the specified behavior
Create a customized merge function with specific merge behavior options. Allows control over how undefined values, defaults, and arrays are handled.
Examples:
// Create a merger that ignores undefined values
const mergeIgnoreUndefined = Obj.mergeWith({ undefined: false })
mergeIgnoreUndefined({ a: 1 }, { a: undefined, b: 2 })
// Returns: { a: 1, b: 2 }
// Create a merger that concatenates arrays
const mergeArrays = Obj.mergeWith({
array: (a, b) => {
a.push(...b)
},
})
mergeArrays({ items: [1, 2] }, { items: [3, 4] })
// Returns: { items: [1, 2, 3, 4] }
[C]
merge
;(<obj1 extends Any, obj2 extends Any>(obj1: obj1, obj2: obj2) => obj1 & obj2)
Deep merge two objects, with properties from the second object overwriting the first. Recursively merges nested objects, but arrays and other non-object values are replaced.
Examples:
Obj.merge({ a: 1, b: 2 }, { b: 3, c: 4 })
// Returns: { a: 1, b: 3, c: 4 }
// Deep merging of nested objects
Obj.merge(
{ user: { name: 'Alice', age: 30 } },
{ user: { age: 31, city: 'NYC' } },
)
// Returns: { user: { name: 'Alice', age: 31, city: 'NYC' } }
// Arrays are replaced, not merged
Obj.merge({ tags: ['a', 'b'] }, { tags: ['c', 'd'] })
// Returns: { tags: ['c', 'd'] }
[C]
mergeWithArrayPush
;(<obj1 extends Any, obj2 extends Any>(obj1: obj1, obj2: obj2) => obj1 & obj2)
Deep merge two objects with special handling for arrays. When both objects have an array at the same path, concatenates them instead of replacing.
Examples:
Obj.mergeWithArrayPush(
{ tags: ['react', 'typescript'] },
{ tags: ['nodejs', 'express'] },
)
// Returns: { tags: ['react', 'typescript', 'nodejs', 'express'] }
// Works with nested arrays
Obj.mergeWithArrayPush(
{ user: { skills: ['js'] } },
{ user: { skills: ['ts'] } },
)
// Returns: { user: { skills: ['js', 'ts'] } }
[C]
mergeWithArrayPushDedupe
;(<obj1 extends Any, obj2 extends Any>(obj1: obj1, obj2: obj2) => obj1 & obj2)
Deep merge two objects with array concatenation and deduplication. When both objects have an array at the same path, concatenates and removes duplicates.
Examples:
Obj.mergeWithArrayPushDedupe(
{ tags: ['react', 'vue', 'react'] },
{ tags: ['react', 'angular'] },
)
// Returns: { tags: ['react', 'vue', 'angular'] }
// Preserves order with first occurrence kept
Obj.mergeWithArrayPushDedupe(
{ ids: [1, 2, 3] },
{ ids: [3, 4, 2, 5] },
)
// Returns: { ids: [1, 2, 3, 4, 5] }
[C]
mergeDefaults
<obj1 extends Any, obj1Defaults extends Partial<obj1>>(obj1: obj1, obj1Defaults: obj1Defaults) => { [_ in keyof(obj1 & obj1Defaults)]: (obj1 & obj1Defaults)[_]; }
Merge default values into an object, only filling in missing properties. Existing properties in the base object are preserved, even if undefined.
Examples:
Obj.mergeDefaults(
{ name: 'Alice', age: undefined },
{ name: 'Unknown', age: 0, city: 'NYC' },
)
// Returns: { name: 'Alice', age: undefined, city: 'NYC' }
// Note: existing properties (even undefined) are not overwritten
// Useful for configuration objects
const config = { port: 3000 }
const defaults = { port: 8080, host: 'localhost', debug: false }
Obj.mergeDefaults(config, defaults)
// Returns: { port: 3000, host: 'localhost', debug: false }
[F]
shallowMergeDefaults
<$Defaults extends object, $Input extends object > (defaults: $Defaults, input: $Input): $Defaults & $Input
Parameters:
defaults
- The default valuesinput
- The input values that override defaults
Returns: Merged object
Shallow merge two objects with later values overriding earlier ones. Useful for providing defaults that can be overridden.
Examples:
const defaults = { a: 1, b: 2, c: 3 }
const input = { b: 20 }
Obj.shallowMergeDefaults(defaults, input) // { a: 1, b: 20, c: 3 }
[F]
spreadShallow
<$Objects extends readonly (object | undefined)[]>(...objects ?: $Objects): { }
Parameters:
objects
- Objects to merge (later objects override earlier ones). Undefined objects are ignored.
Returns: Merged object with undefined values omitted
Shallow merge objects while omitting undefined values. Simplifies the common pattern of conditionally spreading objects to avoid including undefined values that would override existing values.
Examples:
// Instead of:
const config = {
...defaultConfig,
...(userConfig ? userConfig : {}),
...(debug ? { debug: true } : {}),
}
// Use:
const config = Obj.spreadShallow(
defaultConfig,
userConfig,
{ debug: debug ? true : undefined },
)
// undefined values won't override earlier values
Path Utilities
[F]
normalizePropertyPathInput
<pathInput extends PropertyPathInput>(pathInput: pathInput): normalizePropertyPathInput<pathInput>
Parameters:
pathInput
- Either a dot-notation string or array of property names
Returns: An array of property names representing the path
Normalize a property path input to a consistent array format. Accepts either a dot-notation string or an array of property names.
Examples:
Obj.normalizePropertyPathInput('user.address.city')
// Returns: ['user', 'address', 'city']
Obj.normalizePropertyPathInput(['user', 'address', 'city'])
// Returns: ['user', 'address', 'city'] (unchanged)
[C]
parsePropertyPathExpression
;(<expression extends string>(expression: expression) =>
parsePropertyPathExpression<expression>)
Parse a dot-notation property path expression into an array of property names.
Examples:
Obj.parsePropertyPathExpression('user.name')
// Returns: ['user', 'name']
Obj.parsePropertyPathExpression('config.server.port')
// Returns: ['config', 'server', 'port']
Obj.parsePropertyPathExpression('singleProperty')
// Returns: ['singleProperty']
Predicates
[F]
hasNonUndefinedKeys
(object: object): boolean
Parameters:
object
- The object to check
Returns: True if at least one value is not undefined
Check if an object has any non-undefined values.
Examples:
Obj.hasNonUndefinedKeys({ a: undefined, b: undefined }) // false
Obj.hasNonUndefinedKeys({ a: undefined, b: 1 }) // true
Obj.hasNonUndefinedKeys({}) // false
[F]
empty
(): Empty
Returns: An empty object with type Record<string, never>
Create an empty object with proper type. Returns a frozen empty object typed as Empty.
Examples:
const opts = options ?? Obj.empty()
// Type is properly inferred as Empty
const emptyObj = Obj.empty()
type T = typeof emptyObj // Record<string, never>
[F]
isEmpty
(obj: object): boolean
Parameters:
obj
- The object to check
Returns: True if the object has no enumerable properties
Check if an object has no enumerable properties.
Examples:
Obj.isEmpty({}) // true
Obj.isEmpty({ a: 1 }) // false
// Non-enumerable properties are ignored
const obj = {}
Object.defineProperty(obj, 'hidden', { value: 1, enumerable: false })
Obj.isEmpty(obj) // true - non-enumerable properties are ignored
[F]
isEmpty$
<$T extends object>(obj: $T): boolean
Parameters:
obj
- The object to check
Returns: True if the object has no enumerable properties, with type narrowing to Empty
Type predicate that checks if an object has no enumerable properties. Narrows the type to an empty object type.
Examples:
const obj: { a?: number } = {}
if (isEmpty$(obj)) {
// obj is now typed as Empty
}
// Useful in conditional type flows
function processObject<T extends object>(obj: T) {
if (isEmpty$(obj)) {
// obj is Empty here
return 'empty'
}
// obj retains its original type here
}
Shape & Validation
[F]
assert
(value: unknown): void
Parameters:
value
- The value to check
Throws:
- TypeError If the value is not an object
Assert that a value is an object. Throws a TypeError if the value is not an object (including null).
Examples:
function process(value: unknown) {
Obj.assert(value)
// value is now typed as object
console.log(Object.keys(value))
}
[F]
isShape
<type>(spec: Record<PropertyKey, "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function">): (value: unknown) => value is type
Parameters:
spec
- An object mapping property names to their expected typeof results
Returns: A type predicate function that checks if a value matches the shape
Create a type predicate function that checks if a value matches a shape specification. Uses JavaScript's typeof
operator to validate property types.
Examples:
const isUser = isShape<{ name: string; age: number }>({
name: 'string',
age: 'number',
})
isUser({ name: 'Alice', age: 30 }) // true
isUser({ name: 'Bob' }) // false - missing age
isUser({ name: 'Charlie', age: '30' }) // false - age is string
// Can check for functions and other typeof types
const isCallback = isShape<{ fn: Function }>({
fn: 'function',
})
State Management
[F]
setPrivateState
<obj extends Any>(obj: obj, value: object): obj
Parameters:
obj
- The object to attach private state tovalue
- The state object to attach
Returns: The original object with private state attached
Attach private state to an object using a non-enumerable Symbol property. The state is immutable once set and cannot be discovered through enumeration.
Examples:
const user = { name: 'Alice' }
const privateData = { password: 'secret123' }
Obj.setPrivateState(user, privateData)
// user still appears as { name: 'Alice' } when logged
// but has hidden private state accessible via getPrivateState
// Useful for attaching metadata without polluting the object
const config = { timeout: 5000 }
Obj.setPrivateState(config, {
source: 'environment',
timestamp: Date.now(),
})
[F]
getPrivateState
<state extends Any>(obj: object): state
Parameters:
obj
- The object to retrieve private state from
Returns: The private state object
Throws:
- Error if no private state is found on the object
Retrieve private state previously attached to an object with setPrivateState.
Examples:
const user = { name: 'Alice' }
setPrivateState(user, { role: 'admin' })
const privateData = getPrivateState<{ role: string }>(user)
console.log(privateData.role) // 'admin'
// Type-safe private state retrieval
interface Metadata {
createdAt: number
createdBy: string
}
const doc = { title: 'Report' }
setPrivateState(doc, { createdAt: Date.now(), createdBy: 'system' })
const meta = getPrivateState<Metadata>(doc)
// meta is typed as Metadata
Traits
[C]
Eq
Eq<object>
Eq trait implementation for objects.
Provides deep structural equality for objects by recursively comparing properties using their appropriate Eq implementations.
Examples:
import { Obj } from '@wollybeard/kit'
// Basic object equality
Obj.Eq.is({ a: 1, b: 2 }, { a: 1, b: 2 }) // true
Obj.Eq.is({ a: 1, b: 2 }, { a: 1, b: 3 }) // false
Obj.Eq.is({ a: 1 }, { a: 1, b: 2 }) // false (different keys)
// Nested objects
Obj.Eq.is(
{ a: 1, b: { c: 2 } },
{ a: 1, b: { c: 2 } },
) // true
// Mixed types in properties
Obj.Eq.is(
{ a: 1, b: 'hello', c: true },
{ a: 1, b: 'hello', c: true },
) // true
Transformation
[F]
mapEntriesDeep
<$value extends DeepObjectValue>(value: $value, visitor: (key: string, value: DeepObjectValue) => { key: string; value: DeepObjectValue; } | undefined): $value
Parameters:
value
- The value to traverse (can be primitive, object, or array)visitor
- Function called for each object entry. Returnundefined
to keep unchanged, or{key, value}
to transform.
Returns: A new structure with entry transformations applied
Recursively traverse nested object structures and transform key-value pairs (entries).
Performs top-down traversal: The visitor function is called for each object entry BEFORE recursing into the value's children. This allows transforming both keys and values while maintaining consistent traversal semantics with mapValuesDeep.
Visitor Pattern
The visitor receives both the key and value for each object entry:
- Return
undefined
: Keep the entry unchanged and recurse into the value's children - Return
{key, value}
: Replace the entry and recurse into the NEW value's children
Note: Unlike mapValuesDeep, this function does NOT support early exit. The visitor result is always recursed into, whether it's the original or transformed value.
Features
- Key transformations: Rename object keys throughout nested structures
- Value transformations: Transform values based on their keys
- Combined transformations: Change both key and value simultaneously
- Circular reference safe: Automatically detects and marks circular references as
'[Circular]'
- Type preservation: Maintains array and object structures during traversal
Common Use Cases
- Normalizing key naming conventions (e.g., stripping prefixes, camelCase conversion)
- Transforming values based on key patterns
- Sanitizing or filtering object properties recursively
- Renaming keys while preserving nested structure
Comparison with mapValuesDeep
Use mapEntriesDeep when you need to:
- Transform object keys
- Access both key and value in the visitor
- Transform entries at the object level
Use mapValuesDeep when you need to:
- Only transform values (keys unchanged)
- Early exit optimization (stop recursing after match)
- Transform any value type (not just object entries)
Examples:
Key normalization - Strip prefix from keys:
const data = {
$name: 'Alice',
$age: 30,
$address: {
$city: 'NYC',
$zip: '10001',
},
}
Obj.mapEntriesDeep(data, (key, value) =>
key.startsWith('$') ? { key: key.slice(1), value } : undefined)
// { name: 'Alice', age: 30, address: { city: 'NYC', zip: '10001' } }
Value transformation - Uppercase all string values:
const data = {
name: 'alice',
location: {
city: 'nyc',
country: 'usa',
},
}
Obj.mapEntriesDeep(data, (key, value) =>
typeof value === 'string' ? { key, value: value.toUpperCase() } : undefined)
// { name: 'ALICE', location: { city: 'NYC', country: 'USA' } }
Combined transformation - Strip prefix AND uppercase string values:
Obj.mapEntriesDeep(data, (key, value) => {
if (key.startsWith('$')) {
const newKey = key.slice(1)
const newValue = typeof value === 'string' ? value.toUpperCase() : value
return { key: newKey, value: newValue }
}
})
Selective transformation - Only transform specific keys:
Obj.mapEntriesDeep(data, (key, value) => {
if (key === 'password' || key === 'apiKey') {
return { key, value: '[REDACTED]' }
}
})
Works with arrays - Transforms entries in nested arrays:
const users = [
{ $id: 1, $name: 'Alice' },
{ $id: 2, $name: 'Bob' },
]
Obj.mapEntriesDeep(users, (key, value) =>
key.startsWith('$') ? { key: key.slice(1), value } : undefined)
// [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
[F]
mapValuesDeep
(value: any, visitor: (value: any) => any, visited?: WeakSet<WeakKey> = new WeakSet()): any
Parameters:
value
- Any value to traverse (primitive, object, or array)visitor
- Transformation function called for each value. Returnundefined
to continue recursing, or any other value to replace and stop.
Returns: Transformed structure with visitor transformations applied
Recursively traverse and transform values in nested data structures with early exit optimization.
Performs top-down traversal: The visitor function is called for each value BEFORE recursing into its children. This enables the visitor to transform a value and prevent further recursion, which is useful for replacing complex objects with primitives or handling special types.
Early Exit Pattern
The visitor function controls recursion through its return value:
- Return
undefined
: Continue recursing into the original value's children - Return any other value: Use as replacement and STOP recursing into that branch
Features
- Handles all structures: Works with primitives, objects, arrays, and nested combinations
- Circular reference safe: Automatically detects and marks circular references as
'[Circular]'
- Type preservation: Maintains array and object structures during traversal
- Performance: Early exit allows stopping recursion when a match is found
Common Use Cases
- Encoding schema instances to primitives (e.g., for snapshots)
- Replacing Error objects with error messages
- Sanitizing sensitive data in nested structures
- Truncating or formatting string values deeply
- Converting special objects to JSON-serializable forms
Examples:
Schema encoding - Transform schema instances to encoded primitives:
import { Schema as S } from 'effect'
Obj.mapValuesDeep(testData, (v) => {
for (const schema of [FsLoc.FsLoc, User.User]) {
if (S.is(schema)(v)) {
return S.encodeSync(schema)(v) // Replace and stop recursing
}
}
// Return undefined to continue into children
})
// Before: { location: FsLocInstance { ... } }
// After: { location: './src/index.ts' }
Error sanitization - Replace Error objects with messages:
const data = {
result: 'success',
errors: [new Error('Failed'), new Error('Timeout')],
nested: { err: new Error('Deep error') },
}
Obj.mapValuesDeep(data, (v) => {
if (v instanceof Error) return v.message
})
// { result: 'success', errors: ['Failed', 'Timeout'], nested: { err: 'Deep error' } }
String truncation - Limit string lengths throughout a structure:
Obj.mapValuesDeep(data, (v) => {
if (typeof v === 'string' && v.length > 100) {
return v.slice(0, 100) + '...'
}
})
Conditional replacement - Replace specific objects entirely:
Obj.mapValuesDeep(data, (v) => {
// Replace Buffer objects with their base64 representation
if (Buffer.isBuffer(v)) {
return v.toString('base64')
}
})
[F]
mapValues
<rec extends Record<PropertyKey, any>, newValue > (obj: rec, fn: (value: rec[keyof rec], key: keyof rec) => newValue): Record<keyof rec, newValue>
Parameters:
obj
- The object to map values fromfn
- Function to transform each value, receives the value and key
Returns: A new object with transformed values
Create a new object with the same keys but with values transformed by a function.
Examples:
const prices = { apple: 1.5, banana: 0.75, orange: 2 }
const doublePrices = Obj.mapValues(prices, (price) => price * 2)
// Result: { apple: 3, banana: 1.5, orange: 4 }
// Using the key parameter
const data = { a: 1, b: 2, c: 3 }
const withKeys = Obj.mapValues(data, (value, key) => `${key}: ${value}`)
// Result: { a: 'a: 1', b: 'b: 2', c: 'c: 3' }
Type Utilities
[T]
Keyof
type Keyof<$Object extends object> = object extends $Object ? PropertyKey
: (keyof $Object)
Like keyof but returns PropertyKey for object type. Helper type for generic object key operations.
[T]
PolicyFilter
type PolicyFilter<
$Object extends object,
$Key extends Keyof<$Object>,
$Mode extends 'allow' | 'deny',
> = $Mode extends 'allow' ? Pick<$Object, Extract<$Key, keyof $Object>>
: Omit<$Object, Extract<$Key, keyof $Object>>
Filter object properties based on a policy mode and set of keys.
Examples:
type User = { id: number; name: string; email: string; password: string }
// Allow mode: keep only specified keys
type PublicUser = PolicyFilter<User, 'id' | 'name', 'allow'>
// Result: { id: number; name: string }
// Deny mode: remove specified keys
type SafeUser = PolicyFilter<User, 'password', 'deny'>
// Result: { id: number; name: string; email: string }
[T]
PickWhereValueExtends
type PickWhereValueExtends<$Obj extends object, $Constraint> = {
[k in keyof $Obj as $Obj[k] extends $Constraint ? k : never]: $Obj[k]
}
Pick properties from an object where the values extend a given constraint.
Examples:
type User = { name: string; age: number; isActive: boolean; flag: boolean }
type BooleanProps = PickWhereValueExtends<User, boolean>
// Result: { isActive: boolean; flag: boolean }
[T]
SuffixKeyNames
type SuffixKeyNames<$Suffix extends string, $Object extends object> = {
[k in keyof $Object as k extends string ? `${k}${$Suffix}` : k]: $Object[k]
}
Add a suffix to all property names in an object.
Examples:
type T = SuffixKeyNames<'_old', { a: string; b: number }>
// { a_old: string; b_old: number }
[T]
OmitKeysWithPrefix
type OmitKeysWithPrefix<$Object extends object, $Prefix extends string> = {
[k in keyof $Object as k extends `${$Prefix}${string}` ? never : k]:
$Object[k]
}
Omit all keys that start with a specific prefix.
Examples:
type T = OmitKeysWithPrefix<{ _a: string; _b: number; c: boolean }, '_'>
// { c: boolean }
[T]
PickRequiredProperties
type PickRequiredProperties<$T extends object> = {
[k in keyof $T as {} extends Pick<$T, k> ? never : k]: $T[k]
}
Pick only the required (non-optional) properties from an object.
Examples:
type T = PickRequiredProperties<{ a: string; b?: number }> // { a: string }
[T]
RequireProperties
type RequireProperties<$O extends object, $K extends keyof $O> = Ts.Simplify<
$O & { [k in $K]-?: $O[k] }
>
Make specific properties required in an object.
Examples:
type T = RequireProperties<{ a?: string; b?: number }, 'a'>
// { a: string; b?: number }
[T]
PartialOrUndefined
type PartialOrUndefined<$T> = {
[k in keyof $T]?: $T[k] | undefined
}
Make all properties optional and allow undefined values.
Examples:
type T = PartialOrUndefined<{ a: string; b: number }>
// { a?: string | undefined; b?: number | undefined }
[T]
PickOptionalPropertyOrFallback
type PickOptionalPropertyOrFallback<
$Object extends object,
$Property extends keyof $Object,
$Fallback,
> = {} extends Pick<$Object, $Property> ? $Object[$Property] : $Fallback
Pick an optional property or use fallback if required.
Examples:
type T1 = PickOptionalPropertyOrFallback<{ a?: string }, 'a', never> // string
type T2 = PickOptionalPropertyOrFallback<{ a: string }, 'a', never> // never
[T]
OnlyKeysInArray
type OnlyKeysInArray<
$Obj extends object,
$KeysArray extends readonly string[],
> = {
[k in keyof $Obj as k extends $KeysArray[number] ? k : never]: $Obj[k]
}
Pick only the properties from an object that exist in a provided array of keys.
Examples:
type User = { id: number; name: string; age: number; email: string }
type PublicUser = OnlyKeysInArray<User, ['name', 'email']>
// Result: { name: string; email: string }
[T]
GetKeyOr
type GetKeyOr<$T, $Key, $Or> = $Key extends keyof $T ? $T[$Key] : $Or
Get value at key, or return fallback if key doesn't exist.
Examples:
type T1 = GetKeyOr<{ a: string }, 'a', never> // string
type T2 = GetKeyOr<{ a: string }, 'b', never> // never
[T]
GetOrNever
type GetOrNever<$O extends object, $P extends string> = $P extends keyof $O
? $O[$P]
: $P extends `${infer __head__}.${infer __tail__}`
? __head__ extends keyof $O ? GetOrNever<$O[__head__] & object, __tail__>
: never
: never
Get value at key or return never.
Examples:
type T1 = GetOrNever<{ a: string }, 'a'> // string
type T2 = GetOrNever<{ a: string }, 'b'> // never
[T]
keyofOr
type keyofOr<$Obj extends object, $Or> = [keyof $Obj] extends [never] ? $Or
: $Obj[keyof $Obj]
Get the union of all value types from an object, or return fallback if no keys.
Examples:
type T1 = keyofOr<{ a: string; b: number }, never> // string | number
type T2 = keyofOr<{}, 'fallback'> // 'fallback'
[T]
KeysArray
type KeysArray<$Obj extends object> = Array<keyof $Obj>
Create an array type containing the keys of an object.
Examples:
type User = { name: string; age: number; email: string }
type UserKeys = KeysArray<User>
// Result: Array<'name' | 'age' | 'email'>
[T]
KeysReadonlyArray
type KeysReadonlyArray<$Obj extends object> = ReadonlyArray<keyof $Obj>
Create a readonly array type containing the keys of an object.
Examples:
type User = { name: string; age: number; email: string }
type UserKeys = KeysReadonlyArray<User>
// Result: ReadonlyArray<'name' | 'age' | 'email'>
[∩]
StringKeyof
type StringKeyof<$T> = keyof $T & string
Extract only string keys from an object.
Examples:
type T = StringKeyof<{ a: 1; [x: number]: 2 }> // 'a'
[T]
PrimitiveFieldKeys
type PrimitiveFieldKeys<$T> = {
[K in keyof $T]: $T[K] extends
string | number | boolean | bigint | null | undefined ? K
: $T[K] extends Date ? K
: never
}[keyof $T]
Extract keys from an object type that have primitive values. Useful for serialization scenarios where only primitive values can be safely transferred.
Examples:
type User = {
id: number
name: string
createdAt: Date
metadata: { tags: string[] }
isActive: boolean
}
type SerializableKeys = PrimitiveFieldKeys<User>
// Result: 'id' | 'name' | 'createdAt' | 'isActive'
// Note: Date is considered primitive for serialization purposes
[T]
MergeAllShallow
type MergeAllShallow<$Objects extends readonly object[]> = $Objects extends
readonly [infer $First extends object, ...infer $Rest extends object[]]
? $Rest extends readonly [] ? $First
: MergeShallow<$First, MergeAllShallow<$Rest>>
: {}
Recursively merge an array of objects using shallow merge semantics. Each object in the array overrides properties from previous objects.
Examples:
type T = MergeAllShallow<[{ a: string }, { b: number }, { c: boolean }]>
// Result: { a: string; b: number; c: boolean }
[T]
MergeAll
type MergeAll<$Objects extends object[]> = $Objects extends
[infer __first__ extends object, ...infer __rest__ extends object[]]
? __first__ & MergeAll<__rest__>
: {}
Merge an array of object types into a single type using deep merge semantics. Uses TypeScript's intersection type (&
) for merging.
Examples:
type T = MergeAll<[{ a: string }, { b: number }]>
// Result: { a: string; b: number }
[∩]
ReplaceProperty
type ReplaceProperty<$Obj extends object, $Key extends keyof $Obj, $NewType> =
& Omit<$Obj, $Key>
& {
[_ in $Key]: $NewType
}
Replace the type of a specific property in an object.
Examples:
type User = { id: number; name: string; age: number }
type UpdatedUser = ReplaceProperty<User, 'id', string>
// Result: { id: string; name: string; age: number }
[∩]
Replace
type Replace<$Object1, $Object2> = Omit<$Object1, keyof $Object2> & $Object2
Replace properties in an object type with new types. Useful for overriding specific property types.
Examples:
type User = { id: number; name: string; createdAt: Date }
type SerializedUser = Replace<User, { createdAt: string }>
// Result: { id: number; name: string; createdAt: string }
[T]
Writeable
type Writeable<$Obj extends object> = Writable<$Obj>
Make all properties of an object writable (remove readonly modifiers).
Examples:
type ReadonlyUser = { readonly id: number; readonly name: string }
type WritableUser = Writeable<ReadonlyUser>
// Result: { id: number; name: string }
[T]
ToParameters
type ToParameters<$Params extends object | undefined> = undefined extends
$Params ? [params?: $Params]
: $Params extends undefined ? [params?: $Params]
: [params: $Params]
Convert an object to a parameters tuple.
[T]
ToParametersExact
type ToParametersExact<
$Input extends object,
$Params extends object | undefined,
> = IsEmpty<$Input> extends true ? []
: ToParameters<$Params>
Convert an object to parameters tuple with exact matching.
[T]
PropertyKeyToString
type PropertyKeyToString<$Key extends PropertyKey> = $Key extends string ? $Key
: $Key extends number ? `${$Key}`
: never
Convert PropertyKey to string if possible.
[T]
IsEmpty
type IsEmpty<$Obj extends object> = keyof $Obj extends never ? true : false
Type-level check to determine if an object type has no keys.
Examples:
type Empty = IsEmpty<{}> // true
type NotEmpty = IsEmpty<{ a: 1 }> // false
[T]
Empty
type Empty = Record<string, never>
Type for an empty object.
[T]
NoExcessNonEmpty
type NoExcessNonEmpty<$Value extends object, $Constraint> =
IsEmpty<$Value> extends true ? never
: NoExcess<$Constraint, $Value>
Like NoExcess but also requires the object to be non-empty.
Enforces that: 1. Object has at least one property (not empty) 2. Object has no excess properties beyond the constraint
Examples:
type User = { name: string }
type T1 = NoExcessNonEmpty<{ name: 'Alice' }, User> // ✓ Pass
type T2 = NoExcessNonEmpty<{}, User> // ✗ Fail - empty
type T3 = NoExcessNonEmpty<{ name: 'Bob'; age: 30 }, User> // ✗ Fail - excess
[T]
HasOptionalKeys
type HasOptionalKeys<$Obj extends object> = OptionalKeys<$Obj> extends never
? false
: true
Check if an interface has any optional properties.
Examples:
type T1 = HasOptionalKeys<{ a?: string }> // true
type T2 = HasOptionalKeys<{ a: string }> // false
[T]
OptionalKeys
type OptionalKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? K : never
}[keyof T]
Extract keys that are optional in the interface.
Examples:
type Obj = { a: string; b?: number; c?: boolean }
type Optional = OptionalKeys<Obj> // 'b' | 'c'
[T]
RequiredKeys
type RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>
Extract keys that are required in the interface.
Examples:
type Obj = { a: string; b?: number; c?: boolean }
type Required = RequiredKeys<Obj> // 'a'
[T]
HasRequiredKeys
type HasRequiredKeys<$Obj extends object> = RequiredKeys<$Obj> extends never
? false
: true
Check if an interface has any required properties.
Examples:
type T1 = HasRequiredKeys<{ a: string }> // true
type T2 = HasRequiredKeys<{ a?: string }> // false
type T3 = HasRequiredKeys<{ a: string; b?: number }> // true
[T]
HasOptionalKey
type HasOptionalKey<$Object extends object, $Key extends keyof $Object> =
undefined extends $Object[$Key] ? true
: false
Check if a key is optional in an object.
Examples:
type T1 = HasOptionalKey<{ a?: string }, 'a'> // true
type T2 = HasOptionalKey<{ a: string }, 'a'> // false
[T]
IsKeyOptional
type IsKeyOptional<$T extends Undefined.Maybe<object>, $K extends string> =
$K extends keyof $T ? ({} extends Pick<$T, $K> ? true : false)
: false
Check if a key is optional in an object.
Examples:
type T1 = IsKeyOptional<{ a?: string }, 'a'> // true
type T2 = IsKeyOptional<{ a: string }, 'a'> // false
type T3 = IsKeyOptional<{ a: string }, 'b'> // false
[T]
HasKey
type HasKey<$T extends object, $K extends string> = $K extends keyof $T ? true
: false
Check if a key exists in an object.
Examples:
type T1 = HasKey<{ a: string }, 'a'> // true
type T2 = HasKey<{ a: string }, 'b'> // false
Type Utilities
$A - First object type $B - Second object type
[T]
SharedKeys
type SharedKeys<$A extends object, $B extends object> = O.IntersectKeys<$A, $B>
Extract keys that are shared between two object types.
Returns a union of keys that exist in both $A
and $B
. Useful for finding common properties when comparing object types.
Examples:
type A = { a: string; b: number; c: boolean }
type B = { b: string; c: number; d: Date }
type Shared = Obj.SharedKeys<A, B> // "b" | "c"
// No shared keys
type X = { a: string }
type Y = { b: number }
type None = Obj.SharedKeys<X, Y> // never
// All keys shared
type Same = Obj.SharedKeys<{ a: 1; b: 2 }, { a: 3; b: 4 }> // "a" | "b"
Type Utilities
$A - The object type to subtract from $B - The object type whose properties to remove
[T]
SubtractShallow
type SubtractShallow<$A, $B> = Omit<$A, keyof $B>
Subtract properties present in $B from $A (shallow operation).
Returns a new object type containing only properties that exist in $A but not in $B. This is equivalent to Omit<$A, keyof $B>
but expresses the operation as subtraction.
Examples:
type User = { name: string; age: number; email: string }
type Public = { name: string; age: number }
type Private = Obj.SubtractShallow<User, Public> // { email: string }
type Same = Obj.SubtractShallow<User, User> // {}
// Finding what's different between two object types
type Config = { id: string; debug?: boolean }
type Provided = { id: string; invalid: true; typo: string }
type Extra = Obj.SubtractShallow<Provided, Config> // { invalid: true; typo: string }
Type Utilities
$Expected - The expected object type $Actual - The actual object type to compare
[T]
Mismatched
type Mismatched<$Expected extends object, $Actual extends object> = {
[k in SharedKeys<$Expected, $Actual>]: k extends keyof $Expected
? k extends keyof $Actual
? $Expected[k] extends $Actual[k]
? $Actual[k] extends $Expected[k] ? never
: { expected: $Expected[k]; actual: $Actual[k] }
: { expected: $Expected[k]; actual: $Actual[k] }
: never
: never
}
Find properties that exist in both object types but have different types.
For each shared key, compares the types of the properties. If they differ, returns an object with { expected: TypeA, actual: TypeB }
for that key. If types match, returns never
for that key (which can be filtered out with OmitNever).
Examples:
type Expected = { id: string; name: string; count: number }
type Actual = { id: number; name: string; count: string }
type Diff = Obj.Mismatched<Expected, Actual>
// {
// id: { expected: string; actual: number }
// name: never // Types match
// count: { expected: number; actual: string }
// }
// Combined with OmitNever to get only mismatches
type OnlyMismatches = Obj.OmitNever<Obj.Mismatched<Expected, Actual>>
// {
// id: { expected: string; actual: number }
// count: { expected: number; actual: string }
// }
Type Utilities
$Expected - The type defining allowed properties $Actual - The actual type to check for excess properties
[∩]
NoExcess
type NoExcess<$Expected, $Actual> =
& $Actual
& Record<Exclude<keyof $Actual, keyof $Expected>, never>
Enforces that a type has no excess properties beyond those defined in the expected type.
This utility intersects the actual type with a record that marks all excess keys as never
, causing TypeScript to reject values with properties not present in the expected type. Particularly useful in generic contexts where excess property checking is bypassed.
Examples:
type User = { name: string; age: number }
// Standard generic - allows excess properties
function test1<T extends User>(input: T): void {}
test1({ name: 'Alice', age: 30, extra: true }) // ✓ No error (excess allowed)
// With NoExcess - rejects excess
function test2<T extends User>(input: Obj.NoExcess<User, T>): void {}
test2({ name: 'Alice', age: 30, extra: true }) // ✗ Error: 'extra' is never
test2({ name: 'Alice', age: 30 }) // ✓ OK
// Using with optional properties
type Config = { id: string; debug?: boolean }
function configure<T extends Config>(config: Obj.NoExcess<Config, T>): void {}
configure({ id: 'test' }) // ✓ OK - optional omitted
configure({ id: 'test', debug: true }) // ✓ OK - optional included
configure({ id: 'test', invalid: 'x' }) // ✗ Error: 'invalid' is never
Type Utilities
$T - The object type to filter
[T]
OmitNever
type OmitNever<$T> = {
[k in keyof $T as $T[k] extends never ? never : k]: $T[k]
}
Remove all properties with never
type from an object type.
Filters out object properties whose values are never
, leaving only properties with concrete types. Useful for cleaning up conditional type results that use never
as a sentinel value.
Examples:
type Mixed = {
keep1: string
remove: never
keep2: number
alsoRemove: never
}
type Clean = Obj.OmitNever<Mixed>
// { keep1: string; keep2: number }
// Common pattern: conditional properties
type Conditional<T> = {
[K in keyof T]: T[K] extends string ? T[K] : never
}
type Input = { a: string; b: number; c: string }
type OnlyStrings = Obj.OmitNever<Conditional<Input>>
// { a: string; c: string }
Type Utilities
$T - The object type to remove keys from $Keys - Union of keys to remove
[T]
ExcludeKeys
type ExcludeKeys<$T, $Keys> = {
[k in Exclude<keyof $T, $Keys>]: $T[k]
}
Remove specified keys from an object type, with forced evaluation.
Similar to TypeScript's built-in Omit
, but ensures the resulting type is fully evaluated rather than showing as Omit<T, K>
in error messages. This makes type errors more readable by displaying the actual resulting object type.
Examples:
type User = { id: string; name: string; email: string; password: string }
type Public = Obj.ExcludeKeys<User, 'password'>
// { id: string; name: string; email: string }
type Minimal = Obj.ExcludeKeys<User, 'email' | 'password'>
// { id: string; name: string }
// Difference from Omit - better error messages
type WithOmit = Omit<User, 'password'> // Displays as: Omit<User, "password">
type WithExclude = Obj.ExcludeKeys<User, 'password'> // Displays as: { id: string; name: string; email: string }
Type Utilities
$T - The type whose keys should be aligned $Length - The target length for padded keys $Pad - The character to use for padding (defaults to '_')
[T]
AlignKeys
type AlignKeys<$T, $Length extends number, $Pad extends string = '_'> = {
[k in keyof $T as k extends string ? Str.PadEnd<k, $Length, $Pad> : k]: $T[k]
}
Align object keys by padding with a character to a target length.
Pads string keys to the specified length using the given fill character. Non-string keys (symbols, numbers) are left unchanged. Ensures consistent alignment of object keys in IDE displays.
Examples:
type Input = { MESSAGE: string; EXPECTED: number }
type Output = Obj.AlignKeys<Input, 12>
// { MESSAGE_____: string, EXPECTED____: number }
// Custom padding character
type Output2 = Obj.AlignKeys<Input, 12, '.'>
// { MESSAGE.....: string, EXPECTED....: number }
Other
[C]
Type
Type<object>
[F]
pick
<T extends object, K extends keyof T > (obj: T, keys: readonly K[]): Pick < T, K >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value?: undefined, obj?: undefined) => boolean): Partial < $Object >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value: $Object[keyof $Object], obj?: $Object | undefined) => boolean): Partial < $Object >
<T extends object, K extends keyof T > (obj: T, keysOrPredicate: readonly K[] | ((key: keyof T, value?: any, obj?: any) => boolean)): Pick<T, K> | Partial<T>
Parameters:
obj
- The object to pick properties from
Returns: A new object containing only the specified properties
[F]
pick
<T extends object, K extends keyof T > (obj: T, keys: readonly K[]): Pick < T, K >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value?: undefined, obj?: undefined) => boolean): Partial < $Object >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value: $Object[keyof $Object], obj?: $Object | undefined) => boolean): Partial < $Object >
<T extends object, K extends keyof T > (obj: T, keysOrPredicate: readonly K[] | ((key: keyof T, value?: any, obj?: any) => boolean)): Pick<T, K> | Partial<T>
Parameters:
obj
- The object to pick properties from
Returns: A new object containing only the specified properties
[F]
pick
<T extends object, K extends keyof T > (obj: T, keys: readonly K[]): Pick < T, K >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value?: undefined, obj?: undefined) => boolean): Partial < $Object >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value: $Object[keyof $Object], obj?: $Object | undefined) => boolean): Partial < $Object >
<T extends object, K extends keyof T > (obj: T, keysOrPredicate: readonly K[] | ((key: keyof T, value?: any, obj?: any) => boolean)): Pick<T, K> | Partial<T>
Parameters:
obj
- The object to pick properties from
Returns: A new object containing only the specified properties
[F]
omit
<T extends object, K extends keyof T > (obj: T, keys: readonly K[]): Omit < T, K >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value?: undefined, obj?: undefined) => boolean): Partial < $Object >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value: $Object[keyof $Object], obj?: $Object | undefined) => boolean): Partial < $Object >
<T extends object, K extends keyof T > (obj: T, keysOrPredicate: readonly K[] | ((key: keyof T, value?: any, obj?: any) => boolean)): Omit<T, K> | Partial<T>
Parameters:
obj
- The object to omit properties from
Returns: A new object without the specified properties
[F]
omit
<T extends object, K extends keyof T > (obj: T, keys: readonly K[]): Omit < T, K >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value?: undefined, obj?: undefined) => boolean): Partial < $Object >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value: $Object[keyof $Object], obj?: $Object | undefined) => boolean): Partial < $Object >
<T extends object, K extends keyof T > (obj: T, keysOrPredicate: readonly K[] | ((key: keyof T, value?: any, obj?: any) => boolean)): Omit<T, K> | Partial<T>
Parameters:
obj
- The object to omit properties from
Returns: A new object without the specified properties
[F]
omit
<T extends object, K extends keyof T > (obj: T, keys: readonly K[]): Omit < T, K >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value?: undefined, obj?: undefined) => boolean): Partial < $Object >
<$Object extends object>(obj: $Object, predicate: (key: keyof $Object, value: $Object[keyof $Object], obj?: $Object | undefined) => boolean): Partial < $Object >
<T extends object, K extends keyof T > (obj: T, keysOrPredicate: readonly K[] | ((key: keyof T, value?: any, obj?: any) => boolean)): Omit<T, K> | Partial<T>
Parameters:
obj
- The object to omit properties from
Returns: A new object without the specified properties
[I]
partition
interface partition extends
Ts.SimpleSignature.SimpleSignature<[
(
obj: object,
pickedKeys: readonly string[],
) => { picked: object; omitted: object },
]>
{
<$Object extends object, $Key extends keyof $Object>(
obj: $Object,
pickedKeys: readonly $Key[],
): { omitted: Omit<$Object, $Key>; picked: Pick<$Object, $Key> }
}
[T]
getWith
type getWith<
$Path extends PropertyPath,
$Obj extends Any,
> = $Path extends readonly [
infer __key__ extends string,
...infer __tail__ extends readonly string[],
]
? __key__ extends keyof $Obj
? $Obj[__key__] extends Any ? getWith<__tail__, $Obj[__key__]>
: __tail__ extends readonly [] ? $Obj[__key__]
: never // path/object mismatch
: never // path/object mismatch
: $Obj
[T]
entries
type entries<obj extends Any> = {
[K in keyof obj]-?: // Regarding "-?": we don't care about keys being undefined when we're trying to list out all the possible entries
undefined extends obj[K]
? {} extends Pick<obj, K> ? [K, Undefined.Exclude<obj[K]>] // Optional key - remove only undefined, preserve null
: [K, obj[K]] // Required key with undefined - preserve exact type including undefined
: [K, obj[K]] // Required key without undefined - preserve exact type
}[keyof obj][]
[T]
values
type values<$Obj extends object> = $Obj[keyof $Obj][]
[U]
DeepObjectValue
type DeepObjectValue =
| string
| boolean
| null
| number
| DeepObject
| DeepObjectValue[]
A deep object value can be any JSON-serializable value including nested objects and arrays.
[T]
DeepObject
type DeepObject = { [key: string]: DeepObjectValue }
A deep object is a plain object with string keys and deep object values.
[T]
MergeShallow
type MergeShallow<
$Object1 extends Any,
$Object2 extends Any,
__ = {} extends $Object1 ? $Object2
:
& $Object2
// Keys from $Object1 that are NOT in $Object2
& {
[
__k__ in keyof $Object1 as __k__ extends keyof $Object2 ? never
: __k__
]: $Object1[__k__]
},
> = __
[T]
PartialDeep
type PartialDeep<$Type> = $Type extends Array<infer __inner__>
? Array<PartialDeep<__inner__>>
: $Type extends ReadonlyArray<infer __inner__>
? ReadonlyArray<PartialDeep<__inner__>>
: $Type extends Promise<infer __inner__> ? Promise<PartialDeep<__inner__>>
: $Type extends Function ? $Type
: $Type extends object ? {
[key in keyof $Type]?: PartialDeep<$Type[key]>
}
// else
: $Type
[T]
PropertyPathExpression
type PropertyPathExpression = string
[T]
PropertyPath
type PropertyPath = readonly string[]
[U]
PropertyPathInput
type PropertyPathInput = PropertyPathExpression | PropertyPath
[T]
normalizePropertyPathInput
type normalizePropertyPathInput<pathInput extends PropertyPathInput> =
pathInput extends PropertyPathExpression
? parsePropertyPathExpression<pathInput>
: pathInput extends PropertyPath ? pathInput
: never
[C]
PropertyPathSeparator
'.'
The separator character used in property path expressions. Used to split dot-notation paths like 'user.address.city'.
[T]
parsePropertyPathExpression
type parsePropertyPathExpression<$Expression extends string> =
$Expression extends `${infer __key__}.${infer __rest__}`
? [__key__, ...parsePropertyPathExpression<__rest__>]
: [$Expression]
[T]
InferShapeFromPropertyPath
type InferShapeFromPropertyPath<$PropertyPath extends PropertyPath> =
$PropertyPath extends readonly [] ? {}
: _InferShapeFromPropertyPath<$PropertyPath>
[T]
Any
type Any = object
[T]
PropertySignature
type PropertySignature = {
name: string
type: any
optional: boolean
optionalUndefined: boolean
}