Skip to content

Instantly share code, notes, and snippets.

@jjhiggz
Created May 27, 2025 12:55
Show Gist options
  • Save jjhiggz/d4895053850e7213df8609d8cc963822 to your computer and use it in GitHub Desktop.
Save jjhiggz/d4895053850e7213df8609d8cc963822 to your computer and use it in GitHub Desktop.
A great ts-pattern + JSX example
import { match } from 'ts-pattern';
// Define a discriminated union for sub-task states
type SubTaskState =
| { type: 'loading'; subTaskId: number }
| { type: 'completed'; subTaskId: number; result: string }
| { type: 'error'; subTaskId: number; error: string };
// Define a tuple union for task states
type TaskState =
| ['loading', { taskId: number; subTasks: SubTaskState[] }] // Task is loading, with sub-tasks
| ['completed', { taskId: number; result: string; subTasks: SubTaskState[] }] // Task is completed, with result
| ['error', { taskId: number; error: string; subTasks: SubTaskState[] }] // Task failed, with error message
| ['waiting', { taskId: number; waitingFor: string; subTasks: SubTaskState[] }]; // Task is waiting, with sub-tasks
// Discriminated union for managing project states
type ProjectState = {
projectId: number;
tasks: TaskState[];
};
// Component for rendering the task state
const TaskList = ({ state }: { state: ProjectState }) => {
return (
<div>
{state.tasks.map(([type, payload]) =>
match([type, payload])
.with(['loading', { taskId, subTasks }], () => (
<div key={taskId}>
Task {taskId} is loading...
<ul>
{subTasks.map((subTaskState) =>
match(subTaskState)
.with({ type: 'loading' }, ({ subTaskId }) => (
<li key={subTaskId}>Sub-task {subTaskId} is loading...</li>
))
.with({ type: 'completed' }, ({ subTaskId, result }) => (
<li key={subTaskId} style={{ color: 'green' }}>
Sub-task {subTaskId} completed: {result}
</li>
))
.with({ type: 'error' }, ({ subTaskId, error }) => (
<li key={subTaskId} style={{ color: 'red' }}>
Sub-task {subTaskId} failed: {error}
</li>
))
.exhaustive()
)}
</ul>
</div>
))
.with(['completed', { taskId, result, subTasks }], () => (
<div key={taskId} style={{ color: 'green' }}>
Task {taskId} completed: {result}
<ul>
{subTasks.map((subTaskState) =>
match(subTaskState)
.with({ type: 'loading' }, ({ subTaskId }) => (
<li key={subTaskId}>Sub-task {subTaskId} is loading...</li>
))
.with({ type: 'completed' }, ({ subTaskId, result }) => (
<li key={subTaskId} style={{ color: 'green' }}>
Sub-task {subTaskId} completed: {result}
</li>
))
.with({ type: 'error' }, ({ subTaskId, error }) => (
<li key={subTaskId} style={{ color: 'red' }}>
Sub-task {subTaskId} failed: {error}
</li>
))
.exhaustive()
)}
</ul>
</div>
))
.with(['error', { taskId, error, subTasks }], () => (
<div key={taskId} style={{ color: 'red' }}>
Task {taskId} failed: {error}
<ul>
{subTasks.map((subTaskState) =>
match(subTaskState)
.with({ type: 'loading' }, ({ subTaskId }) => (
<li key={subTaskId}>Sub-task {subTaskId} is loading...</li>
))
.with({ type: 'completed' }, ({ subTaskId, result }) => (
<li key={subTaskId} style={{ color: 'green' }}>
Sub-task {subTaskId} completed: {result}
</li>
))
.with({ type: 'error' }, ({ subTaskId, error }) => (
<li key={subTaskId} style={{ color: 'red' }}>
Sub-task {subTaskId} failed: {error}
</li>
))
.exhaustive()
)}
</ul>
</div>
))
.with(['waiting', { taskId, waitingFor, subTasks }], () => (
<div key={taskId}>
Task {taskId} is waiting for: {waitingFor}
<ul>
{subTasks.map((subTaskState) =>
match(subTaskState)
.with({ type: 'loading' }, ({ subTaskId }) => (
<li key={subTaskId}>Sub-task {subTaskId} is loading...</li>
))
.with({ type: 'completed' }, ({ subTaskId, result }) => (
<li key={subTaskId} style={{ color: 'green' }}>
Sub-task {subTaskId} completed: {result}
</li>
))
.with({ type: 'error' }, ({ subTaskId, error }) => (
<li key={subTaskId} style={{ color: 'red' }}>
Sub-task {subTaskId} failed: {error}
</li>
))
.exhaustive()
)}
</ul>
</div>
))
.exhaustive() // Ensure all cases are handled exhaustively
)}
</div>
);
};
// Example Usage
const Example = () => {
const currentState: ProjectState = {
projectId: 101,
tasks: [
['loading', { taskId: 1, subTasks: [{ type: 'loading', subTaskId: 1 }, { type: 'completed', subTaskId: 2, result: 'Success' }] }],
['completed', { taskId: 2, result: 'Completed successfully', subTasks: [{ type: 'completed', subTaskId: 3, result: 'Sub-task complete' }] }],
['error', { taskId: 3, error: 'Some error occurred', subTasks: [{ type: 'error', subTaskId: 4, error: 'Sub-task failed' }] }],
['waiting', { taskId: 4, waitingFor: 'External input', subTasks: [{ type: 'loading', subTaskId: 5 }] }],
],
};
return <TaskList state={currentState} />;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment