branson
branson

Reputation: 55

The difference of function declaration in typescript

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

Answers (2)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

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;

Playground Link

Upvotes: 3

Aplet123
Aplet123

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

Related Questions