我有一个可以返回同步或异步结果的功能
type HookHandler<T> = (context: MyClass<T>) => boolean | Promise<boolean>;
和一个包含该功能列表的类
class MyClass<T> {
constructor(private handlers: Array<HookHandler<T>>) {
}
public invokeHandlers() : boolean | Promise<boolean> {
// invoke each handler and return:
// - Promise<boolean> if exist a handler that return a Promise<T>
// - boolean if all handlers are synchronous
}
}
我想知道是否有机会使打字稿根据给定的处理程序来推断invokeHandlers()
的返回类型。考虑到所有处理程序都是在设计时声明的:
const myClassSync = new MyClass<MyType>([
(ctx) => true,
(ctx) => false
]);
const myClassAsync = new MyClass<MyType>([
async (ctx) => Promise.resolve(true),
async (ctx) => Promise.reject()
]);
const myClassMix = new MyClass<MyType>([
async (ctx) => Promise.resolve(true),
(ctx) => true
]);
是否可以使invokeHandlers()
的返回类型取决于当前给定的处理程序的类型,而无需显式强制转换?例如
// all handlers are sync, infer boolean
const allHandlersAreOk: boolean = myClassSync.invokeHandlers()
// all handlers are async, infer Promise<boolean>
const allAsyncHandlersAreOk: Promise<boolean> = await myClassAsync.invokeHandlers()
// at least one handler is async, infer Promise<boolean>
const allMixedHandlersAreOk: Promise<boolean> = await myClassMix.invokeHandlers()
我显然可以返回一个简单的Promise<boolean>
,但是我会放弃在同步上下文中调用invokeHandlers()
的可能性,而它想避免这种情况。
有什么建议或其他设计选择可以解决问题?谢谢!
答案 0 :(得分:1)
如果您有办法区分处理程序或在运行时以某种方式标识它们,则可以使用重载
function handler(x: number): string;
function handler(y: string): number;
function handler(arg) {
if (typeof arg === 'number') {
return `${arg}`
} else {
return parseInt(arg);
}
}
const inferred = handler(1); // <-- typescript correctly infers string
const alsoInferred = handler('1'); // <-- typescript correctly infers number
因此,如果您可以编写类似这样的内容:
function handler(context: AsyncHandler): Promise<boolean>;
function handler(context: MixedHandlers): Promise<boolean>;
function handler(context: SyncHandlers): boolean:
function handler(context){
// your implementation, maybe instanceof if each type has a class representation
}
TypeScript可以正确推断返回类型。我不确定这是否可能基于您的代码结构,但我想我会分享。阅读更多here,特别是有关重载的部分
答案 1 :(得分:1)
这是我的处理方法:
为每种可能的钩子处理程序添加单独的类型:
type SyncHookHandler = (context: MyClass<any>) => boolean;
type AsyncHookHandler = (context: MyClass<any>) => Promise<boolean>;
type HookHandler = AsyncHookHandler | SyncHookHandler;
然后使MyClass
取决于您使用的HH
的类型HookHandler
。 invokeHandlers
的返回类型可以是conditional type,如果boolean
是HH
,则返回SyncHookHandler
;如果Promise<boolean>
,则返回HH
是AsyncHookHandler
或AsyncHookHandler | SyncHookHandler
:
class MyClass<HH extends HookHandler> {
constructor(private handlers: Array<HH>) { }
public invokeHandlers(): Promise<boolean> extends ReturnType<HH> ?
Promise<boolean> : boolean;
public invokeHandlers(): boolean | Promise<boolean> {
const rets = this.handlers.map(h => h(this));
const firstPromise = rets.find(r => typeof r !== 'boolean');
if (firstPromise) {
return firstPromise; // what do you want to return here
}
// must be all booleans
const allBooleanRets = rets as boolean[];
return allBooleanRets.every(b => b); // what do you want to return here
}
}
我只是在invokeHandlers()
内做了一些愚蠢的实现,以了解您在那做什么。现在您可以看到您的代码按预期运行
const myClassSync = new MyClass([
(ctx) => true,
(ctx) => false
]);
// all handlers are sync, infer boolean
const allHandlersAreOk: boolean = myClassSync.invokeHandlers()
const myClassAsync = new MyClass([
async (ctx) => Promise.resolve(true),
async (ctx) => Promise.reject()
]);
// all handlers are async, infer Promise<boolean>
// note you do not "await" it, since you want a Promise
const allAsyncHandlersAreOk: Promise<boolean> = myClassAsync.invokeHandlers()
const myClassMix = new MyClass([
async (ctx) => Promise.resolve(true),
(ctx) => true
]);
// at least one handler is async, infer Promise<boolean>
// note you do not "await" it, since you want a Promise
const allMixedHandlersAreOk: Promise<boolean> = myClassMix.invokeHandlers()
对您有用吗?
请注意,由于示例代码对通用参数T
没有结构上的依赖性,因此我已经removed it。如果需要,可以将其重新添加到适当的位置,但是我假设问题更多是关于“可以检测到同步”,而不是关于某些泛型。
好的,希望能有所帮助;祝你好运!
答案 2 :(得分:0)
其中一些人可能会兑现承诺是事实。这是 知道的最多TypeScript。
如果不是,则只能在运行时确定所有返回的诺言。
因此答案是否定的,TypeScript无法推断只能在运行时推断的内容。