TypeScript is great at catching mistakes before they reach your users, but sometimes, it catches things that don't feel like mistakes at all. You write what looks like perfectly reasonable code, and TypeScript pushes back with an error about types being too broad. One of the most common places this happens is when you try using a variable as an index in an object.
While building a client invoice web application, I ran into this problem. I had a config object mapping invoice statuses to Tailwind CSS classes, and I wanted to look up the right styles based on a status prop but TypeScript refused. The fix turned out to be two small keywords: keyof typeof.
In this article, I will explain what the problem was, what those keywords do, and why this pattern is worth reaching for whenever you're working with objects in TypeScript with a specific object structure.
The Problem
I have a status badge component and a config object mapping statuses to Tailwind classes:
const statusConfig = {
Paid: {
dot: "bg-emerald-400",
bg: "bg-emerald-50",
text: "text-emerald-600",
},
Pending: {
dot: "bg-amber-400",
bg: "bg-amber-50",
text: "text-amber-600",
},
Draft: {
dot: "bg-slate-400",
bg: "bg-slate-100",
text: "text-slate-500",
},
};
export default function App() {
function StatusBadge({ status }: { status: string }) {
const cfg = statusConfig[status];
return cfg;
}
console.log(StatusBadge("Pending")); // TypeScript shows an error
return (
<h1>Content goes here</h1>
)
}
This is the error TypeScript throws:
statushas an implicit type of 'any' because expression of type 'string' can't be used to index type '{ Paid: ...; Pending: ...; Draft: ...; }'
The issue is that status is typed as a string which is too broad. TypeScript can't guarantee that an arbitrary string will match any of the 3 keys in the statusConfig object. The fix is to use the keyof typeof type in TypeScript.
keyof typeof
The keyof typeof operator extracts the keys of an object as a union type
keyof typeof statusConfig;
// Equivalent to: "Paid" | "Pending" | "Draft"
- typeof statusConfig – This tells TypeScript to get the type of the data in question, which in this case is an object
- keyof – This tells TypeScript to extract all the keys from the object as union types. It will convert the keys in the object into a set of possible options. This way, TypeScript has an idea of the shape of the statusConfig object.
Applying this in the StatusBadge component:
function StatusBadge({ status }: { status: keyof typeof statusConfig }) {
const cfg = statusConfig[status]; // It now works perfectly
return cfg;
}
You could also save this as a type variable to make the code more readable:
type statusType = keyof typeof statusConfig;
function StatusBadge({ status }: { status: statusType }) {
const cfg = statusConfig[status];
return cfg;
}
Now TypeScript knows that status can only ever be "Paid", "Pending", or "Draft", so indexing into statusConfig is safe.
Why Not Just Use a Type Alias?
You could manually define a type like this:
type Status = "Paid" | "Pending" | "Draft";
And just use that directly, but this will create a maintenance burden. If you were to add or rename a key in statusConfig, you have to update the type separately as well. With keyof typeof, the type is derived directly from the object, so it always in sync as the keys are generated automatically for you, making development faster and less error prone.
keyof typeof is the right tool whenever you need to accept a dynamic value for an object, and you are not sure what that value is. It's a small trick but makes TypeScript work with your data instead of fighting against it.
Conclusion
Type errors like this one can feel like TypeScript getting in your way, but they're usually pointing at something real. In this case, that a plain string is too vague to safely index a specific object. keyof typeof gives you a way to be precise without writing out types by hand, keeping your code both safe and easy to maintain.
United States
NORTH AMERICA
Related News
Secret Claude Tracker Shocks Users After Anthropic's Anti-Surveillance Stance
12h ago
EV Batteries Defy Expectations, Last Hundreds of Thousands of Miles
1d ago
GBase 8a Performance Anomaly Case Study: How a Single Parameter Change Sparked a Chain Reaction
1d ago
Who Else Has Inherited a Codebase With Zero Comments and a Prayer?
1d ago
完美的平庸
4h ago