Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* This is the main entry point for all type exports
*/

export { TRANSACTION_STATUSES } from './transaction-status.ts';
export type { TransactionStatus } from './transaction-status.ts';
export { TRANSACTION_STATUSES, isTerminalTransactionStatus } from './transaction-status.ts';
export type { TerminalTransactionStatus, TransactionStatus } from './transaction-status.ts';

export type { Customer } from './customer.ts';

Expand Down
25 changes: 25 additions & 0 deletions src/types/transaction-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,28 @@ export const TRANSACTION_STATUSES = [

/** Union of all valid transaction statuses, pulled from the array above. */
export type TransactionStatus = (typeof TRANSACTION_STATUSES)[number];

const TERMINAL_TRANSACTION_STATUSES = [
'completed',
'refunded',
'expired',
'error',
'no_market',
'too_small',
'too_large',
] as const;

/** Union of transaction statuses that cannot make further progress. */
export type TerminalTransactionStatus = (typeof TERMINAL_TRANSACTION_STATUSES)[number];

/**
* Returns true when a transaction can no longer make progress.
*
* Terminal statuses:
* `completed`, `refunded`, `expired`, `error`, `no_market`, `too_small`, `too_large`
*/
export function isTerminalTransactionStatus(
status: TransactionStatus,
): status is TerminalTransactionStatus {
return (TERMINAL_TRANSACTION_STATUSES as readonly TransactionStatus[]).includes(status);
}
39 changes: 38 additions & 1 deletion tests/types/transaction-status.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { TRANSACTION_STATUSES, type TransactionStatus } from '@/types/index.ts';
import {
isTerminalTransactionStatus,
TRANSACTION_STATUSES,
type TerminalTransactionStatus,
type TransactionStatus,
} from '@/types/index.ts';

describe('TransactionStatus', () => {
// -- runtime checks on the status array --
Expand Down Expand Up @@ -30,6 +35,38 @@ describe('TransactionStatus', () => {
expect(unique.size).toBe(TRANSACTION_STATUSES.length);
});

it('returns the expected result for every valid status', () => {
const terminalStatuses = new Set<TransactionStatus>([
'completed',
'refunded',
'expired',
'error',
'no_market',
'too_small',
'too_large',
]);

for (const status of TRANSACTION_STATUSES) {
expect(isTerminalTransactionStatus(status)).toBe(terminalStatuses.has(status));
}
});

it('acts as a type guard for terminal statuses', () => {
const terminalStatuses = TRANSACTION_STATUSES.filter(isTerminalTransactionStatus);

const narrowed: TerminalTransactionStatus[] = terminalStatuses;

expect(narrowed).toEqual([
'completed',
'refunded',
'expired',
'error',
'no_market',
'too_small',
'too_large',
] satisfies TerminalTransactionStatus[]);
});

// -- compile-time checks (tsc catches these before tests even run) --

it('accepts every valid status', () => {
Expand Down
Loading