Fix handling of functions and arrays in isEmptyObject and isNonEmptyObject

This commit is contained in:
Sindre Sorhus 2026-04-08 04:56:05 +07:00
parent faf700367e
commit 3b40955b02
2 changed files with 24 additions and 3 deletions

View file

@ -510,7 +510,7 @@ export function isEmptyMap(value: unknown): value is Map<never, never> {
}
export function isEmptyObject<Key extends keyof any = string>(value: unknown): value is Record<Key, never> {
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<never> {
@ -656,7 +656,7 @@ export function isNonEmptyMap<Key = unknown, Value = unknown>(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<Key extends keyof any = string, Value = unknown>(value: unknown): value is Record<Key, Value> {
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<T = unknown>(value: unknown): value is Set<T> {
@ -1636,7 +1636,7 @@ export function assertPromise<T = unknown>(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));
}

View file

@ -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<PropertyKey>();
});
test('is.any', () => {