Skip to main content

getFirstKey

Retrieves the first enumerable property key from an object. Returns the first key encountered during iteration over the object's enumerable properties using a for-in loop. This includes both own properties and inherited properties from the prototype chain. Returns undefined if the object has no enumerable properties.

Signature

const getFirstKey: (object: Record<string, any>) => string | undefined

Parameters

NameTypeDescription
object-The object to get the first key from

Returns

The first enumerable property key, or undefined if object is empty

Examples

Basic usage with plain objects

import { getFirstKey } from '@winglet/common-utils';

// Object with multiple properties
const user = { id: 1, name: 'Alice', email: 'alice@example.com' };
const firstKey = getFirstKey(user);
console.log(firstKey); // 'id' (typically, but order not guaranteed)

// Empty object
console.log(getFirstKey({})); // undefined

// Single property object
const single = { onlyKey: 'value' };
console.log(getFirstKey(single)); // 'onlyKey'

Quick emptiness check

function isEmpty(obj: Record<string, any>): boolean {
return getFirstKey(obj) === undefined;
}

console.log(isEmpty({})); // true
console.log(isEmpty({ a: 1 })); // false

// More efficient than Object.keys for emptiness check
const largeObject = Object.fromEntries(
Array.from({ length: 10000 }, (_, i) => [`key${i}`, i])
);

// getFirstKey stops at first property
console.time('getFirstKey');
isEmpty(largeObject); // ~0.001ms
console.timeEnd('getFirstKey');

// Object.keys creates full array
console.time('Object.keys');
Object.keys(largeObject).length === 0; // ~2ms
console.timeEnd('Object.keys');

Working with inherited properties

const parent = { inheritedKey: 'inherited value' };
const child = Object.create(parent);
child.ownKey = 'own value';

// Returns first key (could be own or inherited)
const first = getFirstKey(child);
console.log(first); // 'ownKey' or 'inheritedKey' (order varies)

// To get only own property first key
function getFirstOwnKey(obj: Record<string, any>) {
for (const key in obj) {
if (obj.hasOwnProperty(key)) return key;
}
return undefined;
}
console.log(getFirstOwnKey(child)); // 'ownKey'

Getting a sample property for type checking

function inferValueType(data: Record<string, any>) {
const firstKey = getFirstKey(data);

if (firstKey === undefined) {
return 'empty';
}

const firstValue = data[firstKey];
return typeof firstValue;
}

console.log(inferValueType({ a: 1, b: 2 })); // 'number'
console.log(inferValueType({ x: 'hello' })); // 'string'
console.log(inferValueType({})); // 'empty'

Processing single-property objects

function processSingleProp(obj: Record<string, any>) {
const key = getFirstKey(obj);

if (key === undefined) {
throw new Error('Object is empty');
}

// For single-property objects, first key is the only key
return { key, value: obj[key] };
}

const result = processSingleProp({ status: 'active' });
console.log(result); // { key: 'status', value: 'active' }

Object with numeric keys

// Numeric keys are converted to strings
const numericKeys = {
0: 'zero',
1: 'one',
2: 'two'
};

console.log(getFirstKey(numericKeys)); // '0' (string, not number)

// Mixed numeric and string keys
const mixed = {
2: 'two',
0: 'zero',
'a': 'letter',
1: 'one'
};

// Numeric keys typically come first in ascending order
console.log(getFirstKey(mixed)); // '0' (typically)

Playground

import { getFirstKey } from '@winglet/common-utils';

// Object with multiple properties
const user = { id: 1, name: 'Alice', email: 'alice@example.com' };
const firstKey = getFirstKey(user);
console.log(firstKey); // 'id' (typically, but order not guaranteed)

// Empty object
console.log(getFirstKey({})); // undefined

// Single property object
const single = { onlyKey: 'value' };
console.log(getFirstKey(single)); // 'onlyKey'

Notes

Key Order Considerations:

  • ES2015+ maintains insertion order for string keys
  • Numeric keys are ordered numerically (0, 1, 2, ...)
  • Symbol keys are ordered by insertion after string keys
  • Order may vary in older JavaScript engines
  • Inherited properties appear after own properties

Performance Characteristics:

  • Time Complexity: O(1) best case (has properties)
  • Time Complexity: O(n) worst case (empty object with long prototype chain)
  • Space Complexity: O(1) constant space
  • Stops immediately at first property found
  • Much faster than Object.keys()[0] for non-empty objects

Use Cases:

  • Quick emptiness checks
  • Getting sample key for validation
  • Processing single-property objects
  • Early exit conditions in iterations
  • Performance-critical first key access

Limitations:

  • Property order is not guaranteed in all environments
  • Includes inherited properties (use hasOwnProperty to filter)
  • Returns string even for numeric property names
  • No way to get first Symbol key (symbols not enumerable in for-in)

Alternative Approaches:

// Using Object.keys (creates array)
const firstKey = Object.keys(object)[0];

// Using Object.entries
const [[firstKey]] = Object.entries(object);

// Using Reflect.ownKeys (includes symbols)
const firstKey = Reflect.ownKeys(object)[0];