Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply type inference to mapObject[s.kind] #20190

Closed
mhchem opened this issue Nov 21, 2017 · 3 comments
Closed

Apply type inference to mapObject[s.kind] #20190

mhchem opened this issue Nov 21, 2017 · 3 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@mhchem
Copy link

mhchem commented Nov 21, 2017

TypeScript Version: 2.6.1

Description

I am very impressed by the type inference TypeScript can do with switch and for...else statements.
I miss, however, type inference for map object lookups.

Code

type Named = { kind: "named", id: string };
type Decimal = { kind: "decimal", id: number };
type Hex = { kind: "hex", id: number };
type Entity = Named | Decimal | Hex;
let areaFunctions = {
    "named": function (s: Named) { return "&"+s.id+";"; },
    "decimal": function (s: Decimal) { return "&#"+s.id+";"; },
    "hex": function (s: Hex) { return "&#x"+s.id+";"; },
    "something else": {}
}
// works
let printEntity1 = function (s: Entity) {
    switch (s.kind) {
        case "named": return areaFunctions["named"](s);
            // This works perfectly, TS knows that s can only be to type 'named', here
            // It also knows the function definition and sees that both match
        case "decimal": return areaFunctions["decimal"](s);
        case "hex": return areaFunctions["hex"](s);
    }
}
// does not work
let printEntity2 = function (s: Entity) {
    return areaFunctions[s.kind](s);  // error, although it is equivalent to printEntity1
}
// does not work, although a common JS pattern
let printEntity3 = function (s: Entity) {
    if (areaFunctions[s.kind]) {
        return areaFunctions[s.kind](s);  // type inference does not work
    }
    return "-";
}

Expected behavior:
printEntity2 would compile

Actual behavior:
But it doesn't. I am forced to expand a single line into a long switch statement (just 3 lines here, but a lot more -- and often used -- in my project).

@aluanhaddad
Copy link
Contributor

As a workaround, you can write

let printEntity2 = function <K extends Entity['kind']>(s: Entity & {kind: K}) {
    return areaFunctions[s.kind](s); 
};

@mhegazy
Copy link
Contributor

mhegazy commented Nov 21, 2017

There are multiple features that are not supported today that contribute to blocking this scenario.. first, The main is that a union property is not callable unless all variants have the same signature. so we would need #7294 first to be able to address this issue. Second we will need to distribute the union over the whole call expression, i,.e. the type of areaFunctions[s.kind](s) would be typeof areaFunctions["named"](s) | tyoeof areaFunctions["decimal"](s) | | tyoeof areaFunctions["hex"](s); then we will need to do some narrowing on s in each branch based on the type of s.kind.

#7294 is the main issue, we currentlly have now way of resolving a union call.

@mhegazy mhegazy added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Nov 21, 2017
@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

4 participants