I want to create a core dialog class, so that when adding a new dialog, TS will resolve a dialog type (and it's return types) automatically, basing on the input given. I was able to achieve most of it, but I failed when it comes to the return values.
Each dialog return a Promise
, promise result should base on the dialog
type passed, eg.
- when
T
isPromptDialogOptions
returnPromise<string | number>
, - when
T
isConfirmDialogOptions
returnPromise<boolean>
, - whereas when
T
isMessageDialogOptions
then returnPromise<void>
.
My actual code for creating a dialog (I have marked the lines that produce errors and explained them below due to the length):
let dialogs: DialogOptions[] = [];
newDialog<T extends DialogOptions, R extends InnerDialogType<T>>(dialog: T) : Promise<R> => {
const promise = new Promise<R>(res => {
// Set dialog resolver
dialog.resolver = res; // error #1 (see below)
});
// Create dialog close handler
dialog.closeHandler = (result: R) => { // error #2 (see below)
// Resolve a promise
dialog.resolver(result);
// Close the dialog
// this.closeDialog(dialog);
};
// Store dialog
dialogs = [...dialogs, dialog];
return promise;
}
This code produces two errors:
#1
linedialog.resolver = res;
Type '(value?: R | PromiseLike | undefined) => void' is not assignable to type '((value?: void | undefined) => void) | ((value?: string | number | undefined) => void) | ((value?: boolean | undefined) => void) | undefined'. Type '(value?: R | PromiseLike | undefined) => void' is not assignable to type '(value?: void | undefined) => void'.
#2
linedialog.closeHandler = (result: R) => {
Type '(result: R) => void' is not assignable to type '((result: void) => void) | ((result: string | number) => void) | ((result: boolean) => void)'.
There's clearly an issue with the wrong types being used for handling result in the BaseDialog
.
Question
How can I make BaseDialog.resolver
and BaseDialog.closeHandler
accept the generic type of R
, that is inferred depending on the dialog result type being passed?
Some examples of what I want to achieve:
const confirmDialog : ConfirmDialogOptions = {
title: "Hello",
message: "I'm stuck. Will you help?",
type: DialogType.DIALOG_CONFIRM
};
newDialog(confirmDialog);
- Expected result:
Promise<boolean>
const dialog = {
title: "Invalid",
message: "Invalid dialog is bad!",
type: DialogType.DIALOG_MESSAGE
}
newDialog(dialog);
- Expected result: error, since dialog doesn't inherit from
BaseDialog
const promptDialog : PromptDialogOptions = {
title: "Hello",
message: "Say hello",
maxLength: 10,
type: DialogType.DIALOG_PROMPT
};
newDialog(promptDialog);
- Expected result:
Promise<string | number>
All types used
export const enum DialogType {
DIALOG_MESSAGE,
DIALOG_CONFIRM,
DIALOG_PROMPT
}
export interface BaseDialog<T> {
title: string;
message: string;
type: DialogType;
resolver?: (value?: T) => void;
}
export interface MessageDialogOptions extends BaseDialog<void> { }
export interface ConfirmDialogOptions extends BaseDialog<boolean> { }
export interface PromptDialogOptions extends BaseDialog<string | number> {
maxLength: number;
}
// Union dialogs
export type DialogOptions = MessageDialogOptions | PromptDialogOptions | ConfirmDialogOptions;