Skip to content

Commit afa59fb

Browse files
kakasooclaude
andauthored
fix: prevent deepStrictObjectKeys from recursing into Date objects (#46)
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 <noreply@anthropic.com>
1 parent 6ca00fc commit afa59fb

File tree

2 files changed

+81
-1
lines changed

2 files changed

+81
-1
lines changed

src/functions/DeepStrictObjectKeys.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export function deepStrictObjectKeys<
6060
for (const key of keys) {
6161
if (key in target) {
6262
const value = (target as any)[key];
63-
if (typeof value === 'object' && value !== null) {
63+
if (typeof value === 'object' && value !== null && !(value instanceof Date)) {
6464
const children = deepStrictObjectKeys(value).map((el) => `${key}.${el}`);
6565
response.push(...children);
6666
}

test/features/Function-DeepStrictObjectKeys.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,86 @@ export function test_functions_deep_strict_object_keys_union_date_string() {
9898
}
9999
}
100100

101+
/**
102+
* Tests that deepStrictObjectKeys treats Date as a leaf when nested inside an object.
103+
* Verifies that keys like "meta.createdAt.toISOString" are NOT produced.
104+
*/
105+
export function test_functions_deep_strict_object_keys_date_nested_in_object() {
106+
interface Target {
107+
meta: {
108+
createdAt: Date;
109+
updatedAt: Date;
110+
label: string;
111+
};
112+
id: number;
113+
}
114+
const target = typia.random<Target>();
115+
116+
const elements = typia.misc.literals<DeepStrictObjectKeys<Target, { array: '[*]'; object: '.' }, false>>();
117+
const keys = deepStrictObjectKeys(target);
118+
for (const key of keys) {
119+
ok(elements.includes(key), `${key} is not in ${JSON.stringify(elements)}`);
120+
}
121+
}
122+
123+
/**
124+
* Tests that deepStrictObjectKeys treats Date as a leaf inside arrays.
125+
* Ensures array elements containing Date properties do not recurse into Date methods.
126+
*/
127+
export function test_functions_deep_strict_object_keys_date_in_array_elements() {
128+
interface Target {
129+
events: {
130+
name: string;
131+
occurredAt: Date;
132+
}[];
133+
}
134+
const target: Target = {
135+
events: [
136+
{ name: 'login', occurredAt: new Date() },
137+
{ name: 'logout', occurredAt: new Date() },
138+
],
139+
};
140+
141+
const keys = deepStrictObjectKeys(target);
142+
for (const key of keys) {
143+
ok(!key.includes('toISOString'), `Date method leaked into keys: ${key}`);
144+
ok(!key.includes('getTime'), `Date method leaked into keys: ${key}`);
145+
ok(!key.includes('getFullYear'), `Date method leaked into keys: ${key}`);
146+
}
147+
}
148+
149+
/**
150+
* Tests that deepStrictObjectKeys handles objects with multiple Date fields at different depths.
151+
*/
152+
export function test_functions_deep_strict_object_keys_multiple_dates_at_different_depths() {
153+
interface Target {
154+
topDate: Date;
155+
nested: {
156+
midDate: Date;
157+
deep: {
158+
bottomDate: Date;
159+
value: number;
160+
};
161+
};
162+
}
163+
const target: Target = {
164+
topDate: new Date(),
165+
nested: {
166+
midDate: new Date(),
167+
deep: {
168+
bottomDate: new Date(),
169+
value: 42,
170+
},
171+
},
172+
};
173+
174+
const elements = typia.misc.literals<DeepStrictObjectKeys<Target, { array: '[*]'; object: '.' }, false>>();
175+
const keys = deepStrictObjectKeys(target);
176+
for (const key of keys) {
177+
ok(elements.includes(key), `${key} is not in ${JSON.stringify(elements)}`);
178+
}
179+
}
180+
101181
/**
102182
* Tests that deepStrictObjectKeys correctly handles branding type of string (typia).
103183
*/

0 commit comments

Comments
 (0)