From 3b40955b02fc9a55e55904e3e9bb82ef07c72442 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Wed, 8 Apr 2026 04:56:05 +0700 Subject: [PATCH] Fix handling of functions and arrays in `isEmptyObject` and `isNonEmptyObject` --- source/index.ts | 6 +++--- test/test.ts | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/source/index.ts b/source/index.ts index ee66861..8a510dd 100644 --- a/source/index.ts +++ b/source/index.ts @@ -510,7 +510,7 @@ export function isEmptyMap(value: unknown): value is Map { } export function isEmptyObject(value: unknown): value is Record { - return isObject(value) && !isMap(value) && !isSet(value) && Object.keys(value).length === 0; + return isObject(value) && !isFunction(value) && !isArray(value) && !isMap(value) && !isSet(value) && Object.keys(value).length === 0; } export function isEmptySet(value: unknown): value is Set { @@ -656,7 +656,7 @@ export function isNonEmptyMap(value: unknown): v // TODO: Use `not` operator here to remove `Map` and `Set` from type guard: // - https://github.com/Microsoft/TypeScript/pull/29317 export function isNonEmptyObject(value: unknown): value is Record { - return isObject(value) && !isMap(value) && !isSet(value) && Object.keys(value).length > 0; + return isObject(value) && !isFunction(value) && !isArray(value) && !isMap(value) && !isSet(value) && Object.keys(value).length > 0; } export function isNonEmptySet(value: unknown): value is Set { @@ -1636,7 +1636,7 @@ export function assertPromise(value: unknown, message?: string): as } } -export function assertPropertyKey(value: unknown, message?: string): asserts value is number { +export function assertPropertyKey(value: unknown, message?: string): asserts value is PropertyKey { if (!isPropertyKey(value)) { throw new TypeError(message ?? typeErrorMessage('PropertyKey', value)); } diff --git a/test/test.ts b/test/test.ts index 662e644..c789fdb 100644 --- a/test/test.ts +++ b/test/test.ts @@ -13,6 +13,7 @@ import {expectTypeOf} from 'expect-type'; import ZenObservable from 'zen-observable'; import is, { assert as isAssert, + assertPropertyKey, type AssertionTypeDescription, type Predicate, type Primitive, @@ -1552,6 +1553,11 @@ test('is.emptyObject', () => { assert.ok(is.emptyObject({})); assert.ok(is.emptyObject(new Object())); // eslint-disable-line no-object-constructor assert.strictEqual(is.emptyObject({unicorn: '🦄'}), false); + assert.strictEqual(is.emptyObject(function () {}), false); // eslint-disable-line prefer-arrow-callback + assert.strictEqual(is.emptyObject(() => {}), false); + assert.strictEqual(is.emptyObject(class Foo {}), false); // eslint-disable-line @typescript-eslint/no-extraneous-class + assert.strictEqual(is.emptyObject([]), false); + assert.strictEqual(is.emptyObject(['unicorn']), false); assert.doesNotThrow(() => { isAssert.emptyObject({}); @@ -1562,6 +1568,9 @@ test('is.emptyObject', () => { assert.throws(() => { isAssert.emptyObject({unicorn: '🦄'}); }); + assert.throws(() => { + isAssert.emptyObject(function () {}); // eslint-disable-line prefer-arrow-callback + }); }); test('is.nonEmptyObject', () => { @@ -1572,6 +1581,13 @@ test('is.nonEmptyObject', () => { assert.strictEqual(is.nonEmptyObject(new Object()), false); // eslint-disable-line no-object-constructor assert.ok(is.nonEmptyObject({unicorn: '🦄'})); + assert.strictEqual(is.nonEmptyObject([]), false); + assert.strictEqual(is.nonEmptyObject(['unicorn']), false); + + const functionWithProperty = function () {}; + (functionWithProperty as any).custom = 'value'; + assert.strictEqual(is.nonEmptyObject(functionWithProperty), false); + assert.throws(() => { isAssert.nonEmptyObject({}); }); @@ -1623,6 +1639,11 @@ test('is.propertyKey', () => { assert.strictEqual(is.propertyKey([]), false); assert.strictEqual(is.propertyKey(new Map()), false); assert.strictEqual(is.propertyKey(new Set()), false); + + // AssertPropertyKey should narrow to PropertyKey (string | number | symbol), not just number + const symbolValue: unknown = Symbol('test'); + assertPropertyKey(symbolValue); + expectTypeOf(symbolValue).toEqualTypeOf(); }); test('is.any', () => {