minigame-std
Version:
Mini Game Standard Development Library.
77 lines (68 loc) • 2.57 kB
text/typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Err, Ok, promiseToAsyncResult, type AsyncResult, type Result } from 'happy-rusty';
import { Future } from 'tiny-future';
/**
* 类型工具:判断 API 是否符合 promisify 条件。
*/
export type ValidAPI<T> = T extends (params: infer P) => infer R
? R extends void | Promise<any>
? P extends { success?: any; } | undefined
? true
: P extends { fail?: any; } | undefined
? true
: false
: false
: false;
/**
* 类型工具:提取成功回调参数类型。
*/
export type SuccessType<T> = T extends (params: infer P) => any
? P extends { success?: (res: infer S) => any }
? S
: never
: never;
/**
* 类型工具:提取失败回调参数类型。
*/
export type FailType<T> = T extends (params: infer P) => any
? P extends { fail?: (err: infer E) => any }
? E
: never
: never;
/**
* 将小游戏异步 API 转换为返回 `AsyncResult<T, E>` 的新函数,需要转换的 API 必须是接受可选 `success` 和 `fail` 回调的函数,并且其返回值必须是 `void` 或 `Promise`。
*
* 其中 `T` 为 `success` 回调的参数类型,`E` 为 `fail` 回调的参数类型。
*
* @param api - 小游戏异步 API。
* @returns 返回一个新的函数,该函数返回 `AsyncResult<T, E>`。
*/
export function promisifyWithResult<F extends (...args: any[]) => any, T = SuccessType<F>, E = FailType<F>>(api: F) : ValidAPI<F> extends true
? (...args: Parameters<F>) => AsyncResult<T, E>
: never {
// @ts-expect-error 跳过运行时是否满足转换条件的检查
return (...args: Parameters<F>): AsyncResult<T, E> => {
const future = new Future<Result<T, E>>();
const options = args[0] ?? {};
const { success, fail } = options;
// 强制使用callback的方式调用,即使支持Promise
options.success = (res: T) => {
// success 回调依旧执行
success?.(res);
future.resolve(Ok(res));
};
options.fail = (err: E) => {
// fail 回调依旧执行
fail?.(err);
future.resolve(Err(err));
};
const res = api(options);
// 也支持其他返回Promise的API
if (res instanceof Promise) {
return promiseToAsyncResult(res);
} else if (res != null) {
throw new Error('API must return void or Promise. Otherwise the return value will be discarded.');
}
return future.promise;
};
}