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! 💬