首页 > 解决方案 > 如何使用可选参数重载 TypeScript 中的函数?

问题描述

我有以下代码部分转换为 TypeScript(来自 JavaScript)。

基本上,如果callback存在参数,我总是希望函数本身返回void。否则,返回类型Promise<object>。在此之前有一个可选参数 ( settings)(因此从技术上讲,callback参数可以作为settings参数传入,函数的前几行处理该用例)。

出于向后兼容的目的(并保持代码干燥),我不想创建另一个名为savePromiseor的函数saveCallback并将其分开。我试图弄清楚如何让 TypeScript 足够聪明,以某种方式理解这个逻辑。

type CallbackType<T, E> = (response: T | null, error?: E) => void;

class User {
    save(data: string, settings?: object, callback?: CallbackType<object, string>): Promise<object> | void {
        if (typeof settings === "function") {
            callback = settings;
            settings = undefined;
        }

        if (callback) {
            setTimeout(() => {
                callback({"id": 1, "settings": settings});
            }, 1000);
        } else {
            return new Promise((resolve) => {
                setTimeout(() => {
                    resolve({"id": 1, "settings": settings});
                }, 1000);
            });
        }
    }
}

const a = new User().save("Hello World"); // Should be type Promise<object>, should eventually resolve to {"id": 1, "settings": undefined}
const b = new User().save("Hello World", (obj) => {
    console.log(obj); // {"id": 1, "settings": undefined}
}); // Should be type void
const c = new User().save("Hello World", {"log": true}); // Should be type Promise<object>, should eventually resolve to {"id": 1, "settings": {"log": true}}
const d = new User().save("Hello World", {"log": true}, (obj) => {
    console.log(obj); // {"id": 1, "settings": {"log": true}}
}); // Should be type void

我很确定我想要的类型文件将类似于以下内容。不确定我在这里是否准确。

save(data: string, settings?: object): Promise<object>;
save(data: string, callback: CallbackType<object, string>): void;
save(data: string, settings: object, callback: CallbackType<object, string>): void;

似乎可以通过执行以下操作来处理作为参数用例callback传入的参数:settings

save(data: string, settings?: object | CallbackType<object, string>, callback?: CallbackType<object, string>): Promise<object> | void

但这太混乱了,根据我的经验,TypeScript 似乎不够聪明,无法意识到settings在函数的前 4 行代码之后它始终是一个可选对象。这意味着在调用时callback您必须键入 cast 它,这再次让人感觉非常混乱。

如何使用 TypeScript 实现这一目标?

标签: typescript

解决方案


TL;博士

这是解决方案(进行了一些重构):

type CallbackType<T, E> = (response: T | null, error?: E) => void;
interface ISettings {
  log?: boolean;
}
interface ISaveResult {
  id: number;
  settings: ISettings | undefined;
}

class User {
  save(data: string): Promise<ISaveResult>;
  save(data: string, settings: ISettings): Promise<ISaveResult>;
  save(data: string, callback: CallbackType<ISaveResult, string>): void;
  save(data: string, settings: ISettings, callback: CallbackType<ISaveResult, string>): void;
  save(data: string, settings?: ISettings | CallbackType<ISaveResult, string>, callback?: CallbackType<ISaveResult, string>): Promise<ISaveResult> | void {
    if (typeof settings !== "object" && typeof settings !== "undefined") {
      callback = settings;
      settings = undefined;
    }

    const localSettings = settings; // required for closure compatibility
    if (callback) {
      const localCallback = callback; // required for closure compatibility
      setTimeout(() => {
        localCallback({ id: 1, "settings": localSettings });
      }, 1000);
    } else {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve({ id: 1, "settings": localSettings });
        }, 1000);
      });
    }
  }
}

const a = new User().save("Hello World"); // User.save(data: string): Promise<ISaveResult>

const b = new User().save("Hello World", obj => { 
  console.log(obj); // obj: ISaveResult | null
}); // User.save(data: string, callback: CallbackType<ISaveResult, string>): void

const c = new User().save("Hello World", { "log": true }); // User.save(data: string, settings: ISettings): Promise<ISaveResult>

const d = new User().save("Hello World", { "log": true }, (obj) => {
  console.log(obj); // obj: ISaveResult | null
}); // User.save(data: string, settings: ISettings, callback: CallbackType<ISaveResult, string>): void

码盘

解释

aFunction也是 an Object,因此 TypeScript 无法隐式区分两者。通过创建特定的ISettings接口,您可以让 TypeScript 区分设置对象和回调函数。

最简单的查看方法是查看 TypeScript 输出的错误以及代码流进行时的变量类型,例如(您的代码):

  • settings在条件下悬停时if

    在此处输入图像描述

  • 当悬停settingsif块内时:

    在此处输入图像描述

  • callback赋值错误:

    类型“Function”不可分配给类型“CallbackType”。类型 'Function' 不提供与签名 '(response: object | null, error?: string | undefined): void'.(2322) 的匹配


推荐阅读