TypeScript Event Types

Recently, while debugging a form click event in one of my TypeScript projects, I observed that when event types aren’t clearly defined, it is challenging to debug events. I wasn’t sure how to correctly define button clicks, key presses, or form submissions. Sometimes my code worked, but I didn’t get autocomplete suggestions or helpful error messages when something was wrong.

Later, I learned that giving the right type to each event makes a big difference. It helps you write cleaner code, catch mistakes early, and makes everything easier to understand.

In this article, I’ll explain how to work with different types of events in TypeScript. We’ll learn about various browser events, including clicks and form submissions, custom events, and even events in React.

Understanding Event Types in TypeScript

Basic DOM Events

The simplest way to handle DOM events is by using the built-in event types that TypeScript provides. Here’s a basic example:

// Adding a click event listener to a button
const button = document.querySelector('button');

button.addEventListener('click', (event: MouseEvent) => {
  console.log('Button clicked!');
  console.log('Mouse position:', event.clientX, event.clientY);
});

TypeScript provides several event types that correspond to different DOM events:

  • MouseEvent for mouse-related events
  • KeyboardEvent for keyboard interactions
  • DragEvent for drag-and-drop operations
  • FocusEvent for focus/blur events
  • TouchEvent for touch interactions

Check out: Conditionally Add Property to Object in TypeScript

Typing Event Handlers

When working with event handlers, it’s important to properly type the function. Here’s how I usually do it:

// main.ts

// Defining a typed event handler
function handleClick(event: MouseEvent): void {
  event.preventDefault();
  console.log('Clicked at position:', event.offsetX, event.offsetY);
}

// Using the handler
document.getElementById('myButton')?.addEventListener('click', handleClick);


<-------Index.html code ---------->
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>TypeScript Event Handler</title>
</head>
<body>
  <button id="myButton">Click Me</button>

  <!-- Link to the compiled JavaScript -->
  <script src="main.js"></script>
</body>
</html>

You can also use the generic EventListener type:

const keyHandler: EventListener = (event: Event) => {
  if (event instanceof KeyboardEvent) {
    console.log('Key pressed:', event.key);
  }
};

window.addEventListener('keydown', keyHandler);

Output:

Event types in TypeScript

Working with HTML Element-Specific Events

Different HTML elements trigger different types of events. TypeScript provides specific types for these cases.

Form Events Example

When handling form submissions, the HTMLFormElement and SubmitEvent types come in handy:

//main.ts
const contactForm = document.getElementById('contactForm') as HTMLFormElement;

contactForm.addEventListener('submit', (event: SubmitEvent) => {
  event.preventDefault();

  // Access form data using FormData API
  const formData = new FormData(contactForm);
  const name = formData.get('name') as string;
  const email = formData.get('email') as string;

  console.log(`Submission from ${name} (${email})`);
});

Index.html for UI.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Form Event Example</title>
</head>
<body>
  <form id="contactForm">
    <label>
      Name:
      <input type="text" name="name" required />
    </label><br />
    <label>
      Email:
      <input type="email" name="email" required />
    </label><br />
    <button type="submit">Submit</button>
  </form>

  <!-- Link to compiled TypeScript -->
  <script src="main.js"></script>
</body>
</html>

Output:

Define Events in TypeScript

Check out: Check if an Object Has a Property in TypeScript

Input Events Example

For handling input changes, you can use the HTMLInputElement type:

const zipCodeInput = document.getElementById('zipCode') as HTMLInputElement;

zipCodeInput.addEventListener('input', (event: Event) => {
  const input = event.target as HTMLInputElement;

  // Validate US zip code (5 digits)
  const isValidZip = /^\d{5}$/.test(input.value);

  if (isValidZip) {
    input.classList.remove('error');
    // Perhaps fetch city/state based on zip code
  } else {
    input.classList.add('error');
  }
});

Custom Events in TypeScript

Sometimes the built-in event types aren’t enough, especially when working with custom events. Here’s how I create and type custom events:

Creating Custom Event Types

//Index.ts

interface PaymentCompletedEvent extends CustomEvent {
  detail: {
    transactionId: string;
    amount: number;
    timestamp: Date;
  };
}

function processPayment(amount: number): void {
  const transactionId = 'TXN-' + Math.random().toString(36).substring(2, 10);

  const paymentEvent = new CustomEvent<PaymentCompletedEvent['detail']>('paymentCompleted', {
    detail: {
      transactionId,
      amount,
      timestamp: new Date()
    },
    bubbles: true
  });

  document.dispatchEvent(paymentEvent);
}

document.addEventListener('paymentCompleted', ((event: Event) => {
  const paymentEvent = event as PaymentCompletedEvent;
  const { transactionId, amount, timestamp } = paymentEvent.detail;

  console.log(`💰 Payment of $${amount} completed at ${timestamp.toLocaleString()}`);
  console.log(`🔑 Transaction ID: ${transactionId}`);
}) as EventListener);

document.addEventListener('DOMContentLoaded', () => {
  const button = document.getElementById('payBtn');
  button?.addEventListener('click', () => processPayment(199.99));
});

After this, compile the typescript using npx tsc. Then, to see the output in UI, create an index.html file.

//index.html



<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <h1>Payment Event Demo</h1>
</head>
<body>
  <button id="payBtn">Pay Now</button>

  <script type="module" src="./dist/index.js"></script>
</body>
</html>

Run the bast with npx live-server, then open the browser to http://127.0.0.1:5500/index.html.

Output:

Button Events in TypeScript

Check out: Typescript Iterate Over Records

Event Types in React

If you’re working with React, TypeScript provides specific types for React events.

Basic React Event Types

import React, { useState } from 'react';

const SubscriptionForm: React.FC = () => {
  const [email, setEmail] = useState('');

  // React's ChangeEvent type
  const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setEmail(event.target.value);
  };

  // React's FormEvent type
  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    console.log(`Subscribing email: ${email}`);
    // Call API to subscribe user
  };

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="email">Subscribe to our newsletter:</label>
      <input 
        type="email" 
        id="email" 
        value={email} 
        onChange={handleEmailChange} 
        placeholder="[email protected]" 
        required 
      />
      <button type="submit">Subscribe</button>
    </form>
  );
};

Now, update the App.tsx file.

import React from 'react';
import SubscriptionForm from './SubscriptionForm';

const App = () => (
  <div>
    <h1>Welcome to My Newsletter</h1>
    <SubscriptionForm />
  </div>
);

export default App;

Run the code in the terminal, npm run dev.

Output:

Custom Events in TypeScript

Check out: Do-While Loop in TypeScript

Common React Event Types

Here are some common React event types I use regularly:

//EventDemo.tsx
import React, { useState } from 'react';

const EventDemo: React.FC = () => {
  const [inputValue, setInputValue] = useState('');

  // Mouse event
  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    console.log('Button clicked:', event.currentTarget.name);
  };

  // Keyboard event
  const handleKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
    if (event.key === 'Enter') {
      console.log('Enter key pressed:', inputValue);
    }
  };

  // Focus event
  const handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {
    console.log('Input focused:', event.currentTarget.name);
  };

  // Drag event
  const handleDrag = (event: React.DragEvent<HTMLDivElement>) => {
    console.log('Element is being dragged');
  };

  return (
    <div>
      <h2>React Event Handling Example</h2>

      {/* Focus + Keyboard */}
      <input
        type="text"
        name="demoInput"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        onFocus={handleFocus}
        onKeyDown={handleKeyPress}
        placeholder="Type and press Enter"
      />

      <br /><br />

      {/* Mouse */}
      <button name="submitBtn" onClick={handleClick}>
        Click Me
      </button>

      <br /><br />

      {/* Drag */}
      <div
        draggable
        onDrag={handleDrag}
        style={{
          width: '150px',
          height: '100px',
          backgroundColor: '#b3d9ff',
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
          cursor: 'grab',
        }}
      >
        Drag Me
      </div>
    </div>
  );
};

export default EventDemo;

Update the App.tsx file

import React from 'react';
import EventDemo from './EventDemo';

const App = () => (
  <div>
    <EventDemo />
  </div>
);

export default App;

Output:

React Event Types in TypeScript

Check out: Show Alerts in TypeScript

Using Event Type Assertions

Sometimes TypeScript can’t infer the exact event type, especially when working with third-party libraries. In such cases, type assertions come in handy:

// When TypeScript doesn't know the exact type
someElement.addEventListener('customEvent', (event: Event) => {
  // Type assertion to access custom properties
  const customEvent = event as CustomEvent<{userId: string}>;
  const userId = customEvent.detail.userId;

  fetchUserData(userId).then(userData => {
    // Process user data
  });
});

Generic Event Type Utility

I’ve found creating utility types for events to be very helpful in larger projects:

// A generic event handler type
type EventHandler<T extends Event> = (event: T) => void;

// Usage examples
const clickHandler: EventHandler<MouseEvent> = (event) => {
  // TypeScript knows this is a MouseEvent
  console.log(event.clientX, event.clientY);
};

const keyHandler: EventHandler<KeyboardEvent> = (event) => {
  // TypeScript knows this is a KeyboardEvent
  if (event.ctrlKey && event.key === 's') {
    event.preventDefault();
    saveDocument();
  }
};

Event Delegation with TypeScript

Event delegation is a powerful pattern, and TypeScript can help make it type-safe:

// Event delegation for a list of items
const shoppingList = document.getElementById('shopping-list');

shoppingList?.addEventListener('click', (event: MouseEvent) => {
  const target = event.target as HTMLElement;

  // Check if a delete button was clicked
  if (target.matches('.delete-item')) {
    const itemId = target.closest('li')?.dataset.itemId;

    if (itemId) {
      console.log(`Removing item ${itemId} from shopping list`);
      // Remove the item from the DOM and maybe from a backend
      target.closest('li')?.remove();
    }
  }

  // Check if the item checkbox was clicked
  if (target.matches('input[type="checkbox"]')) {
    const checkbox = target as HTMLInputElement;
    const itemId = checkbox.closest('li')?.dataset.itemId;

    if (itemId) {
      console.log(`Marking item ${itemId} as ${checkbox.checked ? 'completed' : 'pending'}`);
      // Update item status
    }
  }
});

Output:

Define event types in TypeScript

Check out: React’s useContext Hook with TypeScript

Conclusion

Properly typing events in TypeScript significantly improves your development experience and code quality. It provides better autocompletion, catches errors at compile time, and makes your code more self-documenting.

I’ve found that investing time in learning these patterns has made my TypeScript code much more robust and maintainable over the years.

Whether you’re working with DOM events, custom events, or React events, TypeScript has you covered with comprehensive type definitions. By using the techniques I’ve shared, you’ll be able to handle events with confidence in your TypeScript applications.

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.