Reputation: 55
Let's say we have code like this:
interface Vehicle {
bicycle(): string;
car(wheel: number): void;
bus(passanger: number): {
name: string;
age: number;
}[];
}
type Mapper<K extends keyof Vehicle> = (funcName: K, callback: Vehicle[K]) => void;
interface MapperB {
<K extends keyof Vehicle>(name: K, callback: Vehicle[K]): any;
}
declare const caller: Mapper<keyof Vehicle>;
declare const callerB: MapperB
Or typescript playground here.
And when i call caller
and callerB
, caller
can't infer the callback
type according to the first argument. Actually, I found that there is no way to archive that. But callerB
just does everythings well.
caller("bicycle", () => {
})// can't give any intellisense
callerB('bus', (passanger) => {
return [{
name: 'Jack',
age: 11
}]
})// will give perfect hints according to first argument.
So I was wondering what's the difference between those two declaration, it doesn't seems to be a bug🐶.
Upvotes: 2
Views: 113
Reputation: 249516
One is a generic type that happens to be a function (Mapper
), the other is a generic function (MapperB
).
A generic type has its type parameters specified when you declare caller
so no more inference occurs when you call caller
. K
has already been set in stone, and K
will be the union 'bicycle' | 'car' | 'bus'
. So callback
will be typed as Vehicle['bicycle' | 'car' | 'bus']
which will be a union of all function signatures in Vehicle
which will probably be too permissive for what you want.
A generic function has its type parameters specified (or inferred) when calling the function. So it's at that time based on the type of the argument that K
is decided to be just bus
and the callback parameters can be more accurately inferred.
You can declare a generic function with a type alias, but the generic type parameter list must be on the function not on the type:
type Mapper = <K extends keyof Vehicle>(funcName: K, callback: Vehicle[K]) => void;
Upvotes: 3
Reputation: 35512
Your Mapper
type is generic and returns a non-generic function (the type parameters in the function are expanded after receiving a generic type argument), which means K
is keyof Vehicle
and not the specific key that you're after. The MapperB
type is not generic, but instead has a generic function, which means that the type variables are expanded on function call, meaning that K
can be adjusted to be the specific key of Vehicle
.
Upvotes: 0