仅当接收函数也是 return 承诺时,如何输入 return 类型成为承诺?

How to type return type to be a promise only if receiving function also returns a promise?

我正在制作这个愚蠢的函数,它将为任何函数制作一个 try/catch 并且 return 它作为一个包含数据和错误的元组

export type CatchReturn<T> = [data: T | undefined, error: unknown | undefined];

export const tcatch = <T>(
  tryFunc: (() => T) | (() => Promise<T>)
) => {
  try {
    const res = tryFunc();
    if (res instanceof Promise) {
      return res
        .then<CatchReturn<T>>((r) => [r, undefined])
        .catch<CatchReturn<T>>((e) => [undefined, e]);
    } else {
      return [res, undefined] as CatchReturn<T>;
    }
  } catch (e) {
    return [undefined, e] as CatchReturn<T>;
  }
};

函数按预期运行,但我无法正确键入 return 类型。

目前,return 类型为 CatchReturn<T> | Promise<CatchReturn<T>>,但我希望 return 类型仅在 tryFunc: (() => T) 和 [=17 时为 CatchReturn<T> =] 仅当 tryFunc: (() => Promise<T>).

例如:

目前如何运作

const syncRes = tcatch(() => {}) // return type is CatchReturn<T> | Promise<CatchReturn<T>>
const asyncRes = tcatch(async () => {}) // return type is CatchReturn<T> | Promise<CatchReturn<T>>

我希望它如何工作

const syncRes = tcatch(() => {}) // return type should be CatchReturn<T>
const asyncRes = tcatch(async () => {}) // return type should be Promise<CatchReturn<T>>

我尝试在 return 类型中使用条件类型,但总是出错,我不能 100% 确定这是否可行。

这是函数重载的工作。您只需为 promise 声明一个函数签名,为其他函数声明另一个函数签名。然后你的实现 returns 两者的结合:

// signatures
export function tcatch<T>(tryFunc: () => Promise<T>): Promise<CatchReturn<T>>
export function tcatch<T>(tryFunc: () => T): CatchReturn<T>

// implementation
export function tcatch<T>(
  tryFunc: (() => T) | (() => Promise<T>)
): CatchReturn<T> | Promise<CatchReturn<T>> {
  //...
}

这似乎符合您的预期:

const syncRes = tcatch(() => {}) // CatchReturn<void>
const asyncRes = tcatch(async () => {}) // Promise<CatchReturn<void>>

Playground

不是那么优雅的方式,但是...

export type CatchReturn<T> = [data: T | undefined, error: unknown | undefined];

type MaybeMappedPromise<F extends () => unknown> = ReturnType<F> extends Promise<infer T> ? Promise<CatchReturn<T>> : CatchReturn<ReturnType<F>>

function tcatch<T extends unknown, F extends (() => T) | (() => Promise<T>)>(
  tryFunc: F
): MaybeMappedPromise<F> {
  try {
    const res = tryFunc();
    if (res instanceof Promise) {
      return res
        .then<CatchReturn<T>>((r) => [r, undefined])
        .catch<CatchReturn<T>>((e) => [undefined, e]) as MaybeMappedPromise<F>;
    } else {
      return [res, undefined] as MaybeMappedPromise<F>;
    }
  } catch (e) {
    return [undefined, e] as MaybeMappedPromise<F>;
  }
}

const syncRes = tcatch(() => {}) // return type should be CatchReturn<T>
const asyncRes = tcatch(async () => {}) // return type should be Promise<CatchReturn<T>>