高阶函数推断的打字稿条件类型
Typescript conditional types inferred by high order function
我有一个函数可以return同步或异步结果
type HookHandler<T> = (context: MyClass<T>) => boolean | Promise<boolean>;
和一个 class 获取该函数的列表
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()
的 return 类型。考虑到所有处理程序都是在设计时声明的:
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()
的 return 类型依赖于当前给定处理程序的类型而无需显式转换吗?例如
// 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()
我显然可以 return 一个简单的 Promise<boolean>
,但我会放弃在同步上下文中调用 invokeHandlers()
的可能性,它希望避免这种情况。
面对这个问题有什么建议或其他设计选择吗?谢谢!
他们中的一些人可能 return 承诺是事实。这是 TypeScript 可以 知道的最多的。
是否全部 returning promises 只能在 运行 时间确定。
所以答案是否定的,TypeScript 无法推断出只能在 运行 时间推断出的东西。
如果你有办法在运行时区分你的处理程序或以某种方式识别它们,你可以使用重载
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 可以正确推断出 return 类型。根据您的代码结构,我不确定这是否可行,但我想我会分享。阅读更多内容 here,特别是有关重载的部分
以下是我的处理方法:
为每个可能的钩子处理程序提出单独的类型:
type SyncHookHandler = (context: MyClass<any>) => boolean;
type AsyncHookHandler = (context: MyClass<any>) => Promise<boolean>;
type HookHandler = AsyncHookHandler | SyncHookHandler;
然后根据你使用的HookHandler
类型HH
使MyClass
。 invokeHandlers
的 return 类型可以是 conditional type 如果 HH
是 SyncHookHandler
,则计算结果为 boolean
,如果 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。如果您需要它,您可以将它添加回适当的位置,但我假设问题更多是关于检测同步,如果你可以,而不是一些通用类型。
好的,希望对您有所帮助;祝你好运!
我有一个函数可以return同步或异步结果
type HookHandler<T> = (context: MyClass<T>) => boolean | Promise<boolean>;
和一个 class 获取该函数的列表
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()
的 return 类型。考虑到所有处理程序都是在设计时声明的:
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()
的 return 类型依赖于当前给定处理程序的类型而无需显式转换吗?例如
// 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()
我显然可以 return 一个简单的 Promise<boolean>
,但我会放弃在同步上下文中调用 invokeHandlers()
的可能性,它希望避免这种情况。
面对这个问题有什么建议或其他设计选择吗?谢谢!
他们中的一些人可能 return 承诺是事实。这是 TypeScript 可以 知道的最多的。
是否全部 returning promises 只能在 运行 时间确定。
所以答案是否定的,TypeScript 无法推断出只能在 运行 时间推断出的东西。
如果你有办法在运行时区分你的处理程序或以某种方式识别它们,你可以使用重载
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 可以正确推断出 return 类型。根据您的代码结构,我不确定这是否可行,但我想我会分享。阅读更多内容 here,特别是有关重载的部分
以下是我的处理方法:
为每个可能的钩子处理程序提出单独的类型:
type SyncHookHandler = (context: MyClass<any>) => boolean;
type AsyncHookHandler = (context: MyClass<any>) => Promise<boolean>;
type HookHandler = AsyncHookHandler | SyncHookHandler;
然后根据你使用的HookHandler
类型HH
使MyClass
。 invokeHandlers
的 return 类型可以是 conditional type 如果 HH
是 SyncHookHandler
,则计算结果为 boolean
,如果 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。如果您需要它,您可以将它添加回适当的位置,但我假设问题更多是关于检测同步,如果你可以,而不是一些通用类型。
好的,希望对您有所帮助;祝你好运!