How I refactored code
How I Refactored Code to Make It Cleaner, Safer, and More Scalable
Photo by Mark König on Unsplash
Recently, I came across some TypeScript code that worked fine, but it felt verbose, hard to maintain, and not as type-safe as it could be. I decided to refactor it, and the results were a much cleaner, safer, and more scalable solution.
Here’s the journey from the original code to the refactored version, and why the new approach is an improvement.
The Original Code
Codeexport enum Category { Review = "review", NextJs = "nextJs", React = "react", TailwindCss = "tailwindCss", TypeScript = "typeScript", Frontend = "frontend", Backend = "backend", } export const categories = Object.values(Category); export default function getCategory(category: string) { switch (category) { case "review": return Category.Review; case "nextJs": return Category.NextJs; case "react": return Category.React; case "tailwindCss": return Category.TailwindCss; case "typeScript": return Category.TypeScript; case "frontend": return Category.Frontend; case "backend": return Category.Backend; default: return undefined; } } export function getRealCategoryName(name: Category | undefined) { switch (name) { case Category.Review: return "Review"; case Category.NextJs: return "Next.js"; case Category.React: return "React"; case Category.TailwindCss: return "Tailwind CSS"; case Category.TypeScript: return "TypeScript"; case Category.Frontend: return "Frontend"; case Category.Backend: return "Backend"; default: return undefined; } }
Problems with this approach
- Too much boilerplate: Two large switch statements just for mapping values.
- Error-prone: Adding a new category means updating multiple places.
- Loose typing: getCategory accepts string, which means invalid values (like "banana") are only caught at runtime.
The Refactored Code
Here’s the updated version:
Codeconst Category = { Review: "review", "Next.js": "nextJs", React: "react", "Tailwind CSS": "tailwindCss", TypeScript: "typeScript", Frontend: "frontend", Backend: "backend", } as const; export type CategoryLabel = keyof typeof Category; export type CategoryValue = (typeof Category)[CategoryLabel]; const valueToLabel = Object.fromEntries( Object.entries(Category).map(([label, value]) => [value, label]) ) as Record<CategoryValue, CategoryLabel>; export const categoryValues = Object.values(Category) as CategoryValue[]; export function getCategoryLabel(value: CategoryValue): CategoryLabel { return valueToLabel[value]; }
Why This Is Better
1. Single Source of Truth
All categories are defined in one object (Category). Labels and values stay in sync automatically.
2. Type Safety
With:
Codeexport type CategoryValue = (typeof Category)[CategoryLabel];
TypeScript ensures only valid values can be used. Calling getCategoryLabel("banana") is a type error, not a runtime bug.
3. No More Switch Statements
We replaced the giant switch blocks with a reverse mapping built dynamically:
Codeconst valueToLabel = Object.fromEntries(...)
This automatically supports new categories without additional code.
4. Object Mapper Technique
What we did here is essentially applying an object mapper technique. Instead of imperatively coding mappings with switch or if/else, we define a single object (Category) and then programmatically derive other useful mappings (valueToLabel, categoryValues). This turns a lot of boilerplate into data-driven code. It’s a declarative style that scales much better.
5. Less Boilerplate, More Scalability
Adding a new category is now as simple as editing the Category object. Everything else updates itself.
Example Usage
CodegetCategoryLabel("nextJs"); // "Next.js" getCategoryLabel("react"); // "React"
If you try something invalid:
CodegetCategoryLabel("banana"); // ❌ TypeScript error
Final Thoughts
Enums are useful, but in this case, they added unnecessary verbosity. By switching to an as const object with type inference, we ended up with code that is:
- Cleaner
- Safer
- Easier to extend
- Easier to maintain
- Powered by an object mapper technique instead of brittle switch statements
Next time you find yourself writing long switch statements just to map enums, consider whether a const object with inferred types and an object mapper might serve you better.

