From b10d77bd1ba96b569883af80f86c31f211b83143 Mon Sep 17 00:00:00 2001 From: kakasoo Date: Fri, 20 Feb 2026 15:04:25 +0900 Subject: [PATCH] fix: prevent deepStrictObjectKeys from recursing into Date objects Date objects are leaf values in the object tree (via ValueType), but the runtime function was treating them as regular objects to traverse, producing keys like "createdAt.toISOString" and "createdAt.getTime". This created a type/runtime mismatch where the type-level DeepStrictObjectKeys correctly treated Date as a leaf, but the runtime function did not. Added instanceof Date guard alongside the existing object type check to ensure consistent behavior with the type-level implementation. Test coverage added for: - Date as nested leaf inside objects - Date properties inside array elements - Multiple Date fields at different nesting depths Co-Authored-By: Claude Haiku 4.5 --- src/functions/DeepStrictObjectKeys.ts | 2 +- .../features/Function-DeepStrictObjectKeys.ts | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/functions/DeepStrictObjectKeys.ts b/src/functions/DeepStrictObjectKeys.ts index e760add..52a76e0 100644 --- a/src/functions/DeepStrictObjectKeys.ts +++ b/src/functions/DeepStrictObjectKeys.ts @@ -60,7 +60,7 @@ export function deepStrictObjectKeys< for (const key of keys) { if (key in target) { const value = (target as any)[key]; - if (typeof value === 'object' && value !== null) { + if (typeof value === 'object' && value !== null && !(value instanceof Date)) { const children = deepStrictObjectKeys(value).map((el) => `${key}.${el}`); response.push(...children); } diff --git a/test/features/Function-DeepStrictObjectKeys.ts b/test/features/Function-DeepStrictObjectKeys.ts index a56fe04..73f9dd5 100644 --- a/test/features/Function-DeepStrictObjectKeys.ts +++ b/test/features/Function-DeepStrictObjectKeys.ts @@ -98,6 +98,86 @@ export function test_functions_deep_strict_object_keys_union_date_string() { } } +/** + * Tests that deepStrictObjectKeys treats Date as a leaf when nested inside an object. + * Verifies that keys like "meta.createdAt.toISOString" are NOT produced. + */ +export function test_functions_deep_strict_object_keys_date_nested_in_object() { + interface Target { + meta: { + createdAt: Date; + updatedAt: Date; + label: string; + }; + id: number; + } + const target = typia.random(); + + const elements = typia.misc.literals>(); + const keys = deepStrictObjectKeys(target); + for (const key of keys) { + ok(elements.includes(key), `${key} is not in ${JSON.stringify(elements)}`); + } +} + +/** + * Tests that deepStrictObjectKeys treats Date as a leaf inside arrays. + * Ensures array elements containing Date properties do not recurse into Date methods. + */ +export function test_functions_deep_strict_object_keys_date_in_array_elements() { + interface Target { + events: { + name: string; + occurredAt: Date; + }[]; + } + const target: Target = { + events: [ + { name: 'login', occurredAt: new Date() }, + { name: 'logout', occurredAt: new Date() }, + ], + }; + + const keys = deepStrictObjectKeys(target); + for (const key of keys) { + ok(!key.includes('toISOString'), `Date method leaked into keys: ${key}`); + ok(!key.includes('getTime'), `Date method leaked into keys: ${key}`); + ok(!key.includes('getFullYear'), `Date method leaked into keys: ${key}`); + } +} + +/** + * Tests that deepStrictObjectKeys handles objects with multiple Date fields at different depths. + */ +export function test_functions_deep_strict_object_keys_multiple_dates_at_different_depths() { + interface Target { + topDate: Date; + nested: { + midDate: Date; + deep: { + bottomDate: Date; + value: number; + }; + }; + } + const target: Target = { + topDate: new Date(), + nested: { + midDate: new Date(), + deep: { + bottomDate: new Date(), + value: 42, + }, + }, + }; + + const elements = typia.misc.literals>(); + const keys = deepStrictObjectKeys(target); + for (const key of keys) { + ok(elements.includes(key), `${key} is not in ${JSON.stringify(elements)}`); + } +} + /** * Tests that deepStrictObjectKeys correctly handles branding type of string (typia). */