This is a followup to this question and this question.
// return type
interface DataA {
value: number;
}
// return type
interface DataB {
text: string;
}
// return type
interface DataC {
has: boolean;
}
// correct "resolver" has at least one function with a return type
interface ResolverA {
// 'callA' should be suggested because it is a function with a return type
// payload is DataA
callA(...args: any): DataA;
// 'callB' should be suggested because it is a function with a return type
// payload is DataB
callB(): DataB;
}
// correct "resolver" has at least one function with a return type
interface ResolverB {
// 'value' should not be suggested because it is a number
value: number;
// 'resolve' should be suggested because it is a function with a return type
// payload is DataB
resolve(): DataC;
}
// incorrect "resolver" has at least one function with a return typpe
interface ResolverC {
// 'text' should not be suggested because it is a number
text: string;
}
// should be ok because two properties have a correct "resolver" type
interface ResolversShouldWork {
// 'has' should not be suggested because it is a number
has: boolean;
// 'resolverA' should be suggested because ResolverA has functions with an return type.
resolverA: ResolverA;
// 'resolverB' should be suggested because ResolverB has functions with an return type.
resolverB: ResolverB;
// 'resolverC' should not be suggested because Resolver C has no function with an return type.
resolverC: ResolverC;
}
// should be error because no property has a correct "resolver" type
interface ResolversShouldntWorkA {
// 'item' should not be suggested because it is a number
item: object;
}
// should be error because no property has a correct "resolver" type
interface ResolversShouldntWorkB {
// 'resolverC' should not be suggested because Resolver C has no function with an return type.
resolverC: ResolverC;
}
type PickByValue<T, V> = { [K in keyof T as T[K] extends V ? K : never]: T[K] };
type HasProp<T, D = never> = T extends (keyof T extends never ? never : unknown)
? T
: D;
type AcceptableHandler<T> = HasProp<
PickByValue<T, (...args: any) => any>,
{ needsAtLeastOneMethod(): any }
>;
class Handler<
TResolvers extends {
[TResolver in keyof TResolvers]: AcceptableHandler<TResolver>;
},
> {
public send<
TResolver extends Extract<keyof TResolvers, string>,
TMethod extends Extract<keyof TResolvers[TResolver], string>,
TPayload extends TResolvers[TResolver][TMethod] extends (
...args: any
) => infer R
? R
: never,
>(resolver: TResolver, method: TMethod, payload: TPayload) {
//
}
}
const handlerA = new Handler<ResolversShouldWork>(); // should be ok
const handlerB = new Handler<ResolversShouldntWorkA>(); // should be error
const handlerC = new Handler<ResolversShouldntWorkB>(); // should be error
handlerA.send('has', null, null); // 'has' should be error
handlerA.send('resolverA', 'callA', { value: 5 }); // should be ok, payload should be 'DataA'
handlerA.send('resolverA', 'callB', { text: 'hi' }); // should be ok, payload should be 'DataB'
handlerA.send('resolverB', 'resolve', { has: true }); // should be ok, payload should be 'DataC'
handlerA.send('resolverB', 'value', null); // 'value' should be error
handlerA.send('resolverC', null, null); // 'resolverC' should be error
Here is a playground.
I would like Handler
to only accept interfaces with properties of a type having functions with a return type for a payload.
It doesn't matter if the functions have parameters or not.
So if you create a new interface having properties with types having at least one function with a return type it should work as a generic parameter for Handler
as well.
There are some suggestions that should be autocompleted, some not.
The first parameter of handlerA
's function send
should be choosable between resolverA
and resolverB
.
If resolverA
is selected the second parameter should allow callA
and callB
. If callA
is selected the third parameter payload should expect type DataA
. If callB
is selected payload should expect type DataB
.
If resolverB
is selected the second parameter should only allow resolve
. If selected payload should expect type DataC
.