Skip to content

Inserting intersections with 'Function' while narrowing breaks code #26970

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

Open
DanielRosenwasser opened this issue Sep 7, 2018 · 6 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Breaking Change Would introduce errors in existing code Suggestion An idea for TypeScript

Comments

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Sep 7, 2018

@ahejlsberg and I ran into something like this in the RWC. This currently repros on the nightlies.

function foo(x: Object | (() => string)) {
    if (typeof x === "function") {
        // Previously had no errors
        x();
    }
}

Expected: No error

Actual:

[ts] Cannot invoke an expression whose type lacks a call signature. Type '(() => string) | (Object & Function)' has no compatible call signatures.

Potentially related to #25243.

@DanielRosenwasser DanielRosenwasser added the Bug A bug in TypeScript label Sep 7, 2018
@weswigham
Copy link
Member

Mmmm kinda? Previously we only selected applicable union members, while now we also narrow the not-immediately-applicable ones like we do in the non-union case. In the case of primitives and literals, they evaporate into nevers and narrow away, while Object narrows to Object & Function (which would reduce to just Function if we simplified non-primitives) which, given our inability to consider function-ish things as actually potentially callable, causes the issue. So it's an older issue but cropping up in a new place due to the change

@DanielRosenwasser DanielRosenwasser added the Breaking Change Would introduce errors in existing code label Sep 10, 2018
@DanielRosenwasser
Copy link
Member Author

What does not immediately applicable mean? Why does this case work?

function foo(x: { blah: number } | (() => string)) {
    if (typeof x === "function") {
        x()
    }
}

@RyanCavanaugh
Copy link
Member

Object | (() => string) is subject to subtype reduction so this example is pretty sketchy to begin with.

@RyanCavanaugh RyanCavanaugh added this to the TypeScript 3.3 milestone Oct 9, 2018
@weswigham
Copy link
Member

So it's probably worth mentioning that Object & Function and () => string on their own are both callable. The first is resolved as an untyped call, while the second is strong. #27422 is related (which is making the error more clear), and so is #7294, #9239, and #10620 (which would involve removing the error).

@rafsawicki
Copy link

This caught me off guard today, after upgrading to typescript 3.1. I think it's not uncommon to have a parameter which is either a value or a function returning it - in our company we use it often for helper functions in tests. From a logical point of view it's strange that I can't call something I've just checked to be a function, especially since as @DanielRosenwasser showed it still works in some cases. So for me it's definitely a bug and not just something that requires a better error message.

@DanielRosenwasser DanielRosenwasser removed the Bug A bug in TypeScript label Dec 14, 2018
@DanielRosenwasser DanielRosenwasser added Suggestion An idea for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature labels Dec 14, 2018
@DanielRosenwasser DanielRosenwasser removed this from the TypeScript 3.3 milestone Dec 14, 2018
@jakebailey
Copy link
Member

Just looking at old issues; the original repro here was fixed by #29265. Are there more cases to consider?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Breaking Change Would introduce errors in existing code Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants