Let’s face it—naming things is hard. One moment you’re writing userLoggedInEvent
, the next it’s USER_LOGIN_SUCCESS
, and then someone throws in a user-logged-in
just to keep things interesting.
😩 Consistency? What’s that?
Well, TypeScript can actually fix this for us! Let me introduce you to TS-Naming-Convention, a fun little TypeScript trick that makes sure every name follows a structured format, preventing typos, inconsistencies, and headaches. 🚀
🎯 The Problem: Naming Chaos
Imagine you’re tracking events in a shopping cart system. Here’s what could happen in a world without enforced naming conventions:
const event1 = "addedToCart";
const event2 = "cart_item_deleted";
const event3 = "CartItemUpdated";
❌ Inconsistent casing (camelCase? snake_case? PascalCase?!)
❌ Unclear meaning (Was the cart updated successfully? Who knows?)
❌ Impossible to enforce rules (Developers naming things in their own unique way)
Wouldn’t it be great if we could force everyone (including ourselves) to follow a structured format?
🚀 Enter TS-Naming-Convention!
🛠 The TypeScript Naming Convention Superpower
We can enforce structured naming by using TypeScript template literal types. Let’s define a strict naming rule:
// Define allowed values for structured naming
type Feature = 'cart' | 'auth' | 'order';
type Action = 'add' | 'remove' | 'update';
type ObjectType = 'item' | 'quantity' | 'session';
type State = 'success' | 'failed' | 'pending' | undefined;
// Define the type-safe naming format
type NamingConvention<
F extends Feature,
A extends Action,
O extends ObjectType,
S extends State = undefined
> = S extends undefined
? `${F}_${A}_${O}`
: `${F}_${A}_${O}_${S}`;
🚀 This enforces a naming structure:
[Feature]_[Action]_[Object]_[State?]
For example:
type CartAddItem = NamingConvention<'cart', 'add', 'item'>;
// ✅ "cart_add_item"
type CartRemoveItemFailed = NamingConvention<'cart', 'remove', 'item', 'failed'>;
// ✅ "cart_remove_item_failed"
💡 If a name doesn’t match this format, TypeScript won’t allow it!
📌 Step 2: Restrict Allowed Naming Values
Instead of allowing all possible combinations, we define only selected valid names:
export type StrictNamingConvention = NamingConvention<'cart', 'add', 'item'> | NamingConvention<'cart', 'remove', 'item', 'failed'> | NamingConvention<'auth', 'update', 'session', 'pending'>;
Now, only these specific event names are allowed. No more cart_modify_stuff_okay
nonsense! ❌
✅ Step 3: Enforce Naming in a Type-Safe Function
Let’s create a function that ensures only pre-approved event names can be used.
export function trackEvent(
event: StrictNamingConvention,
details?: Record<string, unknown>
) {
switch (event) {
case 'cart_add_item':
break;
case 'cart_remove_item_failed':
break;
case 'auth_update_session_pending':
break;
default:
break;
}
console.log(`Tracking Event: ${event}`, details);
}
✅ Example Usage:
trackEvent('cart_add_item', { itemId: 123 });
trackEvent('auth_update_session_pending');
// ❌ TypeScript Error: Invalid event
// trackEvent('cartItemAdded');
🔹 Why this approach?
- Enforces strictly typed event names.
- Eliminates runtime errors caused by incorrect event names.
- TypeScript prevents invalid names from being passed.
🎯 Why Use This?
✅ No more typos → TypeScript won’t let you mess up!
✅ Better readability → Clear, structured event names.
✅ Prevents inconsistencies → Every name follows a pattern.
✅ Fully TypeScript-powered → No runtime dependencies, just pure type safety.
By leveraging TypeScript’s type system, you can say goodbye to naming chaos and enforce a clean, structured convention across your entire codebase.
🚀 What’s Next?
This technique isn't just for event names—it works for:
-
API route naming (
api_get_orders_list_success
) -
Redux action types (
auth_update_token_success
) -
Error codes (
error_validation_input_failed
) -
State keys (
cart_remove_item_pending
)
Check out the code?
on GitHub: TS-Naming-Convention. 🚀
Would you use TypeScript to enforce structured naming? Let’s chat in the comments! 💬