Skip to main content

TypeScript Tips

Get the most out of TypeScript with Saga Bus.

Type Inference​

The DSL automatically infers message types:

const saga = createSagaMachine<OrderState, OrderMessages>()
.on('PaymentCaptured')
.handle(async (msg, state, ctx) => {
// TypeScript knows:
// - msg is PaymentCaptured
// - msg.transactionId exists
// - state is OrderState
console.log(msg.transactionId); // ✅ No error
return state;
})

Message Union Types​

Define a union of all messages:

type OrderMessages =
| OrderSubmitted
| PaymentCaptured
| PaymentFailed
| OrderShipped;

// The DSL will type-check handler message types

State Interface​

Extend SagaState for your state:

import type { SagaState } from '@saga-bus/core';

interface OrderState extends SagaState {
orderId: string;
status: OrderStatus;
// ... your fields
}

Generic Handlers​

For reusable handler logic:

function createAuditHandler<TState extends SagaState>() {
return async (msg: unknown, state: TState, ctx: SagaContext) => {
ctx.setMetadata('lastProcessed', new Date().toISOString());
return state;
};
}

Discriminated Unions​

Use discriminated unions for type narrowing:

type OrderStatus =
| { status: 'pending' }
| { status: 'paid'; transactionId: string }
| { status: 'shipped'; trackingNumber: string };

// TypeScript narrows based on status
if (state.status === 'paid') {
console.log(state.transactionId); // ✅ Available
}

Strict Null Checks​

Saga Bus works best with strict mode:

// tsconfig.json
{
"compilerOptions": {
"strict": true,
"strictNullChecks": true
}
}