Created
May 27, 2025 12:55
-
-
Save jjhiggz/d4895053850e7213df8609d8cc963822 to your computer and use it in GitHub Desktop.
A great ts-pattern + JSX example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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