【问题标题】:TypeScript: Detect generic return typeTypeScript:检测通用返回类型
【发布时间】:2019-12-29 17:36:19
【问题描述】:

我想创建一个核心对话框类,以便在添加新对话框时,TS 将根据给定的输入自动解析对话框类型(及其返回类型)。我能够实现大部分目标,但在返回值方面我失败了。

每个对话框都返回一个Promise,promise 结果应该基于传递的dialog 类型,例如。

  • TPromptDialogOptions时返回Promise<string | number>
  • TConfirmDialogOptions 时返回Promise<boolean>
  • 而当TMessageDialogOptions 然后返回Promise<void>

我创建对话框的实际代码(由于篇幅原因,我已经标记了产生错误的行并在下面解释它们):

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;
}

此代码产生两个错误:

  • #1 线dialog.resolver = res;

Type '(value?: R | PromiseLike | undefined) => void' 不是 可分配给类型 '((value?: void | undefined) => void) | ((价值?: 字符串 |号码 |未定义)=> 无效)| ((值?:布尔值|未定义) => 无效)|不明确的'。类型 '(value?: R | PromiseLike | undefined) => void' 不可分配给类型 '(value?: void | undefined) => void'。

  • #2线dialog.closeHandler = (result: R) =&gt; {

类型 '(result: R) => void' 不可分配给类型 '((result: void) => 无效)| ((结果:字符串|数字)=> void)| ((result: boolean) => void)'.

BaseDialog 中用于处理结果的错误类型显然存在问题。

问题

如何使BaseDialog.resolverBaseDialog.closeHandler 接受R 的泛型类型,这取决于传递的对话框result 类型?

我想要实现的一些例子:

const confirmDialog : ConfirmDialogOptions = {
   title: "Hello",
   message: "I'm stuck. Will you help?",
   type: DialogType.DIALOG_CONFIRM
};

newDialog(confirmDialog); 
  • 预期结果:Promise&lt;boolean&gt;
const dialog = {
    title: "Invalid",
    message: "Invalid dialog is bad!",
    type: DialogType.DIALOG_MESSAGE
}

newDialog(dialog);
  • 预期结果:错误,因为对话框不继承自 BaseDialog
const promptDialog : PromptDialogOptions = {
   title: "Hello",
   message: "Say hello",
   maxLength: 10,
   type: DialogType.DIALOG_PROMPT
};

newDialog(promptDialog);
  • 预期结果:Promise&lt;string | number&gt;

使用的所有类型

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;

【问题讨论】:

    标签: javascript typescript generics types


    【解决方案1】:

    为什么不简单呢?

    function newDialog<R>(dialog: BaseDialog<R>): Promise<R> {
       ...
    

    这将使编译器清楚 R 和 T 用于相同的对话框子类型(您当前的函数声明无法表达,导致您提到的编译器错误)。

    如果您选择联合类型来强制对话框实际上是已知类型之一,您可以使用overloads 更好地完成此操作:

    function newDialog(dialog: MessageDialog): Promise<void>;
    function newDialog(dialog: ConfirmDialog): Promise<boolean>;
    function newDialog(dialog: PromptDialog): Promise<string | number>;
    function newDialog<R>(dialog: BaseDialog<R>): Promise<R> {
      // implementation here
    }
    

    顺便说一句,作为调用者,我可能更喜欢单独的命名方法而不是使用枚举指定类型:

    newConfirmDialog({
      title: 'Really launch rockets?',
      text: 'Once launched, they can not be recalled.'
    })
    

    【讨论】:

    • 感谢您的解决方案!我没有使用重载的主要原因是我无法将它们放入界面中,我需要它主要是为了 React 的可访问性和功能,因为我希望这个“对话框存储”成为一个真正的 React 钩子。
    猜你喜欢
    • 2020-01-07
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 2018-11-16
    • 1970-01-01
    • 2021-11-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多