Repository: toss/es-toolkit
File: src/object/findKey.ts (TypeScript)

Code before:
/**
 * Finds the key of the first element in the object that satisfies the provided testing function.
 *
 * @param obj - The object to search.
 * @param predicate - The function to execute on each value in the object. It takes three arguments:
 *   - value: The current value being processed in the object.
 *   - key: The key of the current value being processed in the object.
 *   - obj: The object that findKey was called upon.
 * @returns The key of the first element in the object that passes the test, or undefined if no element passes.
 *
 * @example
 * const users = {
 *   'barney':  { 'age': 36, 'active': true },
 *   'fred':    { 'age': 40, 'active': false },
 *   'pebbles': { 'age': 1,  'active': true }
 * };
 * findKey(users, function(o) { return o.age < 40; }); => 'barney'
 */
export function findKey<T extends PropertyKey, K>(
  obj: Record<T, K>,
  predicate: (value: K, key: T, obj: Record<T, K>) => boolean
): T | undefined {
  return (Object.keys(obj) as Array<T>).find(key => predicate(obj[key], key, obj));
}


Review comment (suggestion):
```suggestion
export function findKey<T extends Record<any, any>>(
  obj: T,
  predicate: (value: T[keyof T], key: keyof T, obj: T) => boolean
): keyof T | undefined {
```

I guess this type signature is more accurate than the original one. 

---

Note that `foo` is inferred as `number | string` in the first one, but is accurately inferred as `number` in the second one.

### Old interface

```ts
declare function findKey<T extends PropertyKey, K>(
  obj: Record<T, K>,
  predicate: (value: K, key: T, obj: Record<T, K>) => boolean
): T | undefined

findKey({ foo: 1, bar: '1' }, (val, key, obj) => {
  // foo is string | number here
  obj.foo
})
```

<img width="760" alt="image" src="https://github.com/user-attachments/assets/b9e7ef23-34c4-47f5-a5a0-4524183b1c69">

### New interface

```ts
declare function findKey<T extends Record<any, any>>(
  obj: T,
  predicate: (value: T[keyof T], key: keyof T, obj: T) => boolean
): T | undefined

findKey({ foo: 1, bar: '1' }, (val, key, obj) => {
  // foo is number here
  obj.foo
})
```

<img width="794" alt="image" src="https://github.com/user-attachments/assets/3b773482-949d-4be1-a3aa-bdae56a4d0be">


Code after:
/**
 * Finds the key of the first element in the object that satisfies the provided testing function.
 *
 * @param obj - The object to search.
 * @param predicate - The function to execute on each value in the object. It takes three arguments:
 *   - value: The current value being processed in the object.
 *   - key: The key of the current value being processed in the object.
 *   - obj: The object that findKey was called upon.
 * @returns The key of the first element in the object that passes the test, or undefined if no element passes.
 *
 * @example
 * const users = {
 *   'barney':  { 'age': 36, 'active': true },
 *   'fred':    { 'age': 40, 'active': false },
 *   'pebbles': { 'age': 1,  'active': true }
 * };
 * findKey(users, function(o) { return o.age < 40; }); => 'barney'
 */
export function findKey<T extends Record<any, any>>(
  obj: T,
  predicate: (value: T[keyof T], key: keyof T, obj: T) => boolean
): keyof T | undefined {
  const keys = Object.keys(obj) as Array<keyof T>;

  return keys.find(key => predicate(obj[key], key, obj));
}