Urgent.News

What's breaking now, across thousands of outlets.

Tech

Beyond Promise<any>: Designing a Type-Safe Modal API

await modal.open(...) improves control flow. But if the result is any , the most important part of the contract is still unchecked. In the previous article , I described a modal interaction as an asynchronous operation: Input ↓ [ user interaction ] ↓ Result That naturally leads to an API like: const result = await modal . open ( renameReportModal , input ); That immediately raises the question…

The abstract model for a modal interaction is an asynchronous operation: input to the modal, resulting in a result. This leads to an API where a modal is opened with a type of input and a promise is awaited for the resulting type. However, this approach does not ensure the contract is enforced. The question remains: what type does 'result' have? If the answer is 'any', the control flow is improved, but the contract remains unchecked.

A modal manager can expose a promise API and still lose information. Consider this example:

const result = await showModal('rename-report', { reportId: report.id });

Here, the 'result' variable is of type 'any'. This allows any number of expressions to compile:

result.name;

result.nmae;

result.whatever;

The compiler cannot determine if the modal returns 'name'; if the field was renamed; if the caller made a typo; or if the modal returns a completely different shape. The promise itself is not the crucial part. The crucial part is preserving the relationship between the modal definition, input type, result type, and call site.

To address this, start with a generic Modal<TInput, TResult>. For instance, a rename modal receives an interface RenameReportInput with properties 'reportId' and 'currentName', both of type string. The modal can produce one of two domain outcomes: a 'renamed' result with a new name, or a 'cancelled' result. This modal conceptually is:

Modal<RenameReportInput, RenameReportResult>

The producer, or the modal component, receives typed input and a typed close function. In React Modal Manager, the component receives the typed input and close function:

function RenameReportModal({ input, close }: ModalComponentProps<RenameReportInput, RenameReportResult>)

Inside the component, 'input.currentName' is known to be a string, and 'close()' only accepts a valid RenameReportResult. Attempting to close with an invalid status or missing properties results in a compile-time error.

The producer side of the interaction is checked. To carry the contract further, create the modal definition layer. This ensures the relationship between modal definition, input type, result type, and call site is preserved throughout the API.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Tech

More from Friday 25 September →