If I comprehend correctly, your objective is to restrict the type parameter passed to RegisterableClass
to have methods that are all asynchronous, meaning they return promises.
If this is the case, you can achieve it like this:
type AllMethodsAreAsync<I> = {
[K in keyof I]: I[K] extends (...args: any) => infer R ?
R extends Promise<any> ? I[K] : (...args: any) => Promise<R> :
I[K]
}
export abstract class RegisterableClass<I extends AllMethodsAreAsync<I>> {
// ... something involving I, ideally
}
The type function AllMethodsAreAsync<I>
will be the same as I
if I
is an object type with function-valued properties that return promises. However, if I
contains function-valued properties that do not return promises, the corresponding property in AllMethodsAreAsync<I>
will be modified to return a promise instead.
Then, if
I extends AllMethodsAreAsync<I>
meets the generic constraint, everything is fine. If not, you will receive an error indicating exactly which part of
I
is causing the issue. Like this:
// adding this so standalone example works
type User = { u: string };
interface IOkayService {
someConstant: number;
getAllUsers(): Promise<User[]>;
}
type OkayService = RegisterableClass<IOkayService>; // okay
interface IBadService {
someConstant: number;
getAllUsers(): Promise<User[]>;
getSomethingSynchronously(x: string): number;
}
type BadService = RegisterableClass<IBadService>; // error!
// ~~~~~~~~~~~
// Type 'IBadService' does not satisfy the constraint 'AllMethodsAreAsync<IBadService>'.
// Types of property 'getSomethingSynchronously' are incompatible.
// Type '(x: string) => number' is not assignable to type '(...args: any) => Promise<number>'.
// Type 'number' is not assignable to type 'Promise<number>'.
Playground link
Is this in alignment with your needs? I wish you the best of luck!