How to Find a Key by Value in a TypeScript Dictionary

To find a key by value in a TypeScript dictionary, get the keys with Object.keys(), type them as keyof, and call find() to return the first key whose value matches. If nothing matches, you get undefined:

const key = (Object.keys(plans) as (keyof typeof plans)[]).find((k) => plans[k] === 15.49);

Nothing below is pseudo-code: each lookup went through tsc --strict and then Node, using TypeScript 7.0.2 and Node.js v22.22.2, and the printed results are copied from my console.

I’ll also show how to get every matching key, search a Map, build a reverse map, match object values, handle no match, and work with enums.

Find a key by value with Object.keys() and find()

Here’s a dictionary of streaming plan prices, searched with Object.keys(), with Object.entries(), and with a reusable generic helper:

const plans = {
  basic: 9.99,
  standard: 15.49,
  premium: 22.99,
};

// 1. Object.keys() + find(), keys typed as keyof
const key = (Object.keys(plans) as (keyof typeof plans)[]).find(
  (k) => plans[k] === 15.49
);
console.log("Object.keys + find:   ", key);

// 2. Object.entries() + find(), key and value together
const entry = Object.entries(plans).find(([, price]) => price === 22.99);
console.log("Object.entries + find:", entry?.[0]);

// 3. Reusable generic helper
function findKeyByValue<T extends object>(dict: T, value: T[keyof T]): keyof T | undefined {
  return (Object.keys(dict) as (keyof T)[]).find((k) => dict[k] === value);
}
console.log("generic helper:       ", findKeyByValue(plans, 9.99));
console.log("no match:             ", findKeyByValue(plans, 12.99));

Output:

Object.keys + find:    standard
Object.entries + find: premium
generic helper:        basic
no match:              undefined
Command Prompt output of TypeScript finding a dictionary key by value with Object.keys, Object.entries and a generic findKeyByValue helper
Each approach returns the matching plan name, and a price that isn’t in the dictionary gives undefined.

The helper returns keyof T | undefined, so the result is "basic" | "standard" | "premium", not just any string. If keyof is new to you, my post on keyof with string keys in TypeScript explains the type.

Object.entries() gives you the pair, but its key is a plain string. Prefer the keyof version when you’ll index the object with it.

Why does TypeScript reject plans[k] inside find()?

Object.keys() is typed as string[] on purpose, because an object can carry extra properties at runtime. Index a typed object with a plain string and strict mode stops you:

const plans = { basic: 9.99, standard: 15.49, premium: 22.99 };

const key = Object.keys(plans).find((k) => plans[k] === 15.49);

Output:

keybyvalue_ts7053.ts(3,44): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ basic: number; standard: number; premium: number; }'.
  No index signature with a parameter of type 'string' was found on type '{ basic: number; standard: number; premium: number; }'.
Command Prompt output of tsc error TS7053 when indexing a TypeScript object with a string key from Object.keys inside find
TS7053: a string can’t index an object whose keys are only basic, standard and premium.

The cast as (keyof typeof plans)[] fixes it. A Record<string, number> doesn’t need it, since it already accepts any string key, as the next example shows.

Get all keys that share the same value

find() stops at the first match. When several keys can hold the same value, like two Chicago airports, switch to filter() to get every matching key:

const airports: Record<string, string> = {
  ORD: "Chicago",
  MDW: "Chicago",
  LAX: "Los Angeles",
  JFK: "New York",
  LGA: "New York",
  DFW: "Dallas",
};

const first = Object.keys(airports).find((code) => airports[code] === "Chicago");
const all = Object.keys(airports).filter((code) => airports[code] === "Chicago");

console.log("find()   ->", first);
console.log("filter() ->", all);
console.log("no match ->", Object.keys(airports).filter((c) => airports[c] === "Boston"));
console.log("any Dallas airport?", Object.values(airports).includes("Dallas"));

Output:

find()   -> ORD
filter() -> [ 'ORD', 'MDW' ]
no match -> []
any Dallas airport? true

An empty array means no match, which is easier to handle than undefined. If you only need a yes or no, Object.values().includes() is enough. I cover more callback patterns in filtering arrays in TypeScript.

Find a key by value in a TypeScript Map

A Map has get() for keys but nothing for values. A for...of loop over its entries returns as soon as it finds a match, and spreading into an array lets you use find():

const employees = new Map<number, string>([
  [1001, "Emma Johnson"],
  [1002, "Liam Smith"],
  [1003, "Olivia Brown"],
]);

function findMapKey<K, V>(map: Map<K, V>, value: V): K | undefined {
  for (const [key, v] of map) {
    if (v === value) return key;      // stops at the first match
  }
  return undefined;
}

console.log("for...of loop: ", findMapKey(employees, "Liam Smith"));

const id = [...employees].find(([, name]) => name === "Olivia Brown")?.[0];
console.log("spread + find: ", id, typeof id);
console.log("not in the Map:", findMapKey(employees, "Noah Davis"));

Output:

for...of loop:  1002
spread + find:  1003 number
not in the Map: undefined

The key comes back as a number: Map keys keep their real type, while object keys are always strings. I compare both in my TypeScript key value pair guide, and here’s how to merge Maps in TypeScript.

Build a reverse map for repeated lookups

Each search by value scans every entry. For frequent lookups, build a value-to-key Map once and call get():

const skuToProduct: Record<string, string> = {
  "SKU-1001": "Wireless Mouse",
  "SKU-1002": "USB-C Hub",
  "SKU-1003": "Laptop Stand",
  "SKU-1004": "USB-C Hub",
};

// One key per value: built once, then every lookup is a get()
const productToSku = new Map(
  Object.entries(skuToProduct).map(([sku, name]) => [name, sku])
);
console.log("Laptop Stand ->", productToSku.get("Laptop Stand"));
console.log("USB-C Hub    ->", productToSku.get("USB-C Hub"), "(last duplicate wins)");

// Every key per value: group with Map.groupBy (ES2024)
const productToSkus = Map.groupBy(Object.entries(skuToProduct), ([, name]) => name);
console.log("USB-C Hub    ->", productToSkus.get("USB-C Hub")?.map(([sku]) => sku));

Output:

Laptop Stand -> SKU-1003
USB-C Hub    -> SKU-1004 (last duplicate wins)
USB-C Hub    -> [ 'SKU-1002', 'SKU-1004' ]
Command Prompt output of a TypeScript reverse Map from product name to SKU and Map.groupBy keeping every SKU for a duplicate value
The simple reverse map keeps only the last SKU for USB-C Hub; Map.groupBy keeps both.

Duplicate values are the catch: the plain reverse map silently overwrites earlier keys, and it won’t update itself, so rebuild it when the dictionary changes. Building lookups from a list is covered in converting an array to a dictionary in TypeScript.

Find a key when the values are objects

Strict equality on objects compares references, not contents. A copy with identical fields won’t match, so compare a property or every field instead:

interface Customer {
  name: string;
  city: string;
  state: string;
}

const customers: Record<string, Customer> = {
  C101: { name: "Ava Martinez", city: "Austin", state: "TX" },
  C102: { name: "Noah Wilson", city: "Denver", state: "CO" },
  C103: { name: "Mia Thompson", city: "Seattle", state: "WA" },
};

const target: Customer = { name: "Noah Wilson", city: "Denver", state: "CO" };
const ids = Object.keys(customers);

console.log("=== on a copy:  ", ids.find((id) => customers[id] === target));
console.log("by one property:", ids.find((id) => customers[id].name === target.name));

function sameCustomer(a: Customer, b: Customer): boolean {
  return (Object.keys(a) as (keyof Customer)[]).every((k) => a[k] === b[k]);
}
console.log("every field:    ", ids.find((id) => sameCustomer(customers[id], target)));

Output:

=== on a copy:   undefined
by one property: C102
every field:     C102

Match on a unique field, like an email or account number, when you can. sameCustomer only checks top-level fields; nested objects need a recursive deep-equality function.

What if no key matches the value?

Every lookup here can return undefined, and TypeScript makes you deal with it before you use the key:

const plans = { basic: 9.99, standard: 15.49, premium: 22.99 };

const match = (Object.keys(plans) as (keyof typeof plans)[]).find((k) => plans[k] === 12.99);
console.log(plans[match].toFixed(2));

Output:

keybyvalue_undefined_index.ts(4,19): error TS2538: Type 'undefined' cannot be used as an index type.

Narrow the result first with === undefined, or a ?? default. Guards written with the is keyword in TypeScript narrow the same way:

const plans = { basic: 9.99, standard: 15.49, premium: 22.99 };
type Plan = keyof typeof plans;

function findPlan(price: number): Plan | undefined {
  return (Object.keys(plans) as Plan[]).find((k) => plans[k] === price);
}

for (const price of [22.99, 12.99]) {
  const match = findPlan(price);
  if (match === undefined) {
    console.log(`No plan costs $${price.toFixed(2)}`);
  } else {
    console.log(`$${plans[match].toFixed(2)} is the ${match} plan`);   // match is Plan here
  }
}

console.log("with a default:", findPlan(12.99) ?? "custom");

// The falsy trap: an empty-string key is a real match
const codes: Record<string, string> = { "": "none", A: "active" };
const k = Object.keys(codes).find((c) => codes[c] === "none");
console.log(`found ${JSON.stringify(k)}, but !k says missing:`, !k);

Output:

$22.99 is the premium plan
No plan costs $12.99
with a default: custom
found "", but !k says missing: true

Avoid if (!key), since an empty-string key is a real match that looks missing. When you just want any entry instead of a match, see getting the first element from a TypeScript dictionary.

Find an enum key by value in TypeScript

Numeric enums get a reverse mapping, so OrderStatus[1] returns the member name directly. String enums don’t, so you search their keys like a dictionary. TypeScript enums explained covers the basics:

enum OrderStatus {
  Pending,
  Shipped,
  Delivered,
}

console.log("numeric reverse mapping:", OrderStatus[1]);
console.log("numeric out of range:   ", OrderStatus[7]);
console.log("Object.keys(OrderStatus):", Object.keys(OrderStatus));

enum Carrier {
  UPS = "ups_ground",
  FedEx = "fedex_home",
  USPS = "usps_priority",
}

console.log("string enum, no reverse:", (Carrier as Record<string, string>)["fedex_home"]);

const fromApi: string = "fedex_home";
const carrierKey = (Object.keys(Carrier) as (keyof typeof Carrier)[]).find(
  (k) => Carrier[k] === fromApi
);
console.log("string enum key:        ", carrierKey);
console.log("Object.keys(Carrier):   ", Object.keys(Carrier));

Output:

numeric reverse mapping: Shipped
numeric out of range:    undefined
Object.keys(OrderStatus): [ '0', '1', '2', 'Pending', 'Shipped', 'Delivered' ]
string enum, no reverse: undefined
string enum key:         FedEx
Object.keys(Carrier):    [ 'UPS', 'FedEx', 'USPS' ]
Command Prompt output of TypeScript numeric enum reverse mapping and finding a string enum key by value with Object.keys and find
The numeric enum maps 1 back to Shipped; the string enum needs Object.keys() plus find().

Watch two traps: an out-of-range number gives undefined although it’s typed as string, and Object.keys() on a numeric enum lists the numbers too.

Going the other way? See converting a string to an enum in TypeScript or converting an enum to a number.

Which way to find a key by value should you use?

SituationUseReturns
Typed object, one match(Object.keys(o) as (keyof typeof o)[]).find()keyof or undefined
Several keys share a valueObject.keys(o).filter()Array, empty if none
Data in a Mapfor...of loop or [...map].find()Original key type or undefined
Many lookups by valueReverse Map built onceget() result
Object valuesCompare a unique propertyKey or undefined
String enumObject.keys(Enum).find()Member name or undefined

Ready for more dictionary work? Try one of these next:

Frequently asked questions

How do I find a key by value in a TypeScript object?

Use (Object.keys(obj) as (keyof typeof obj)[]).find((k) => obj[k] === value). It returns the first matching key or undefined.

Why do I get error TS7053 with Object.keys()?

Object.keys() returns string[], and a plain string can’t index an object with fixed keys. Cast the array to (keyof typeof obj)[].

How do I get every key with the same value?

Use filter() instead of find(). It returns every matching key, or an empty array.

How do I find a key by value in a Map?

Loop with for (const [k, v] of map) and return on the first match, or use [...map].find(([, v]) => v === value)?.[0].

How do I get an enum key from its value?

Numeric enums support Enum[value]. For string enums, search Object.keys(Enum) with find(), since they have no reverse mapping.

Where is Object.keys() documented?

See Object.keys() on MDN.