Skip to content

Comments

fix: preserve required properties when using onlyDefinedProperties#83

Merged
mcollina merged 3 commits intomainfrom
fix/onlyDefinedProperties-types
Feb 19, 2026
Merged

fix: preserve required properties when using onlyDefinedProperties#83
mcollina merged 3 commits intomainfrom
fix/onlyDefinedProperties-types

Conversation

@mcollina
Copy link
Member

@mcollina mcollina commented Feb 7, 2026

Summary

Fixes #82

When using deepmerge({ onlyDefinedProperties: true }), the runtime correctly ignores undefined values from the source and preserves target values. However, the TypeScript types didn't reflect this behavior - merging Partial<T> into T would produce a type with optional properties even though at runtime those undefined values are skipped.

Before this fix:

interface Options { apiUrl: string; timeout: number }
type PartialOptions = Partial<Options>

const full: Options = { apiUrl: 'url', timeout: 100 }
const partial: PartialOptions = { timeout: 200 }

const result = deepmerge({ onlyDefinedProperties: true })(full, partial)
// result.apiUrl was typed as `string | undefined` ❌

After this fix:

const result = deepmerge({ onlyDefinedProperties: true })(full, partial)
// result.apiUrl is typed as `string` ✅
// Result is assignable to Options ✅

Changes

  • Added new type definitions:

    • DeepMergeDefinedFn and DeepMergeAllDefinedFn function types
    • DeepMergeDefined<T, U> type that preserves required properties from target
    • IsMergeableObject<T> helper that works with Partial types (the original { [key: string]: unknown } check fails for optional properties)
    • Function overloads that return the new types when onlyDefinedProperties: true
  • Added comprehensive type tests for:

    • Basic partial merging
    • Deep partial merging
    • all mode with onlyDefinedProperties

Test plan

  • All 147 unit tests pass
  • All type tests pass (npm run test:typescript)
  • New type tests verify the fix works for the reported use case

🤖 Generated with Claude Code

When using `deepmerge({ onlyDefinedProperties: true })`, the runtime
correctly ignores undefined values from the source and preserves target
values. However, the TypeScript types didn't reflect this behavior -
merging `Partial<T>` into `T` would produce a type with optional
properties even though at runtime those undefined values are skipped.

This fix adds new type definitions:
- `DeepMergeDefinedFn` and `DeepMergeAllDefinedFn` function types
- `DeepMergeDefined<T, U>` type that preserves required properties
- `IsMergeableObject<T>` helper that works with Partial types
- Function overloads for `onlyDefinedProperties: true`

Now `deepmerge({ onlyDefinedProperties: true })(fullObj, partialObj)`
correctly returns the full type instead of a partially optional type.

Fixes #82

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
mcollina and others added 2 commits February 8, 2026 00:48
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Functions should not be treated as mergeable objects. This fixes
an issue where function properties (like callbacks) were being
incorrectly merged as objects, resulting in `{}` instead of
preserving the function type.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@mcollina mcollina merged commit d88a94d into main Feb 19, 2026
17 checks passed
@mcollina mcollina deleted the fix/onlyDefinedProperties-types branch February 19, 2026 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Questioning the way how you deep merge a partial JSON into the original JSON?

1 participant