Define TS Types for your Laravel Echo Events

Define TS Types for your Laravel Echo Events

·

3 min read

Hey guys,

I've been working on a Laravel project (personal) for fun, focusing on the latest cool stuff: Laravel 11 & Reverb (from Broadcasting) ⭐️.

Using Laravel Broadcasting means you should use the client library provided by Laravel - Laravel Echo, to save a lot of your time setting up stuff 😎.

Even though Laravel Echo has been written in TypeScript, however, it doesn't give us any opportunity or battery to "type" our events 💣.

So that's why we have this topic and I'll show you how to define the types for your events 😉.

Before going deeper

I suppose you already have some knowledge about:

🥹🥹🥹

The Define Types for Laravel Echo

Note: I'm demonstrating on the PresenceChannel (private channel with auth & more features).

Extending the "PresenceChannel"

I'll create a file called echo.ts and extend the PresenceChannel interface

type BaseEvent = {
  type: string;
  payload: unknown;
};

export interface StrictPresenceChannel<Event extends BaseEvent>
  extends PresenceChannel {
  listen<EventType extends Event['type']>(
    event: EventType,
    callback: (data: Extract<Event, { type: EventType }>['payload']) => void
  ): this;
}

This would be our base interface wrapper for the PresenceChannel.

In the real-life app, we would have multiple channels for communicating between client & server.

Each channel should extend the base interface and have its own Events declaration.

Defining Events

Following the BaseEvent structure, we need to define all of the Events that would be sent to the client.

For a sample chat app, I've added these events:

type ChatMessageAddedEvent = {
    type: 'ChatMessageAdded';
    payload: {
        fromUser: {
            id: string; // ULID
        },
        chat: {
            id: string;
            message: string;
        }
    }
}

type ChatImageAddedEvent = {
    type: 'ChatImageAdded';
    payload: {
        fromUser: {
            id: string; // ULID
        },
        chat: {
            id: string;
            imageUrl: string;
        }
    }
}

type ChatEvent = ChatMessageAddedEvent | ChatImageAddedEvent;

Don't forget to add a final type - a union of all events 😉, we'll use it in the part below.

Create a Channel and Extend the StrictPresenceChannel

I created a ChatChannel interface and added the ChatEvent union type.

export interface ChatChannel extends StrictPresenceChannel<ChatEvent> {}

A function to join the Typed-Channel

// getEchoInstance() is simply returning the new Echo({...})

export const getChatChannel = (channelId: string):  => {
  return getEchoInstance().join(channelId) as ChatChannel;
};

Here we have to hack a bit - using as to force the type.

This is the shortcut for a quick win & hassle-free while it won't result in any error or failure.

Use the Typed-Channel instance 😎

const channel = getRoomChannel(myChatChannel.id);

channel.listen('ChatMessageAdded', (data) => {
    // access data. & IDE will suggest the fields for you 
}).listen('ChatImageAdded', (data) => {
    // access data. & IDE will suggest the fields for you 
});

Then you're all set 😎 IDEs now will handle auto-suggestion, eslint/tsc can do the type check stuff in build time 💪

An example from my project:

Notes

Note: this is purely defining types, it won't guarantee that your data is 100% valid and it could go wrong from the end user 🙀.

I'll make another topic for schemas definition & data validation for Laravel Echo soon, ensuring the data is legit to use ⭐️.

Stay tuned 🚀

Conclusion

Well, that's basically it for defining types for your Broadcast Events 😎.

Have fun guys!