Skip to content

Strictly check callback parameters #18976

New issue

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

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

Already on GitHub? # to your account

Merged
merged 6 commits into from
Oct 6, 2017
Merged

Conversation

ahejlsberg
Copy link
Member

@ahejlsberg ahejlsberg commented Oct 5, 2017

With this PR we fix checking of the return position of callback parameters in --strictFunctionTypes mode. Previously we'd never check this position strictly (because of #15104) but now we check it strictly (i.e. contravariantly) for callback parameters of non-methods. For example:

// Compile with --strictFunctionTypes
declare let f1: (cb: (x: Animal) => Animal) => void;
declare let f2: (cb: (x: Dog) => Dog) => void;
f1 = f2;  // Error, but previously wasn't
f2 = f1;  // Error

The way the fix works is that once we've decided to check a parameter position strictly, we avoid the code path for callback parameters added in #15104. Basically, when we're checking a parameter strictly, we don't need to make exceptions for callbacks.

Fixes #18963 (at least the parts that we can fix).

@ahejlsberg ahejlsberg requested review from sandersn and mhegazy October 5, 2017 21:03
@mhegazy
Copy link
Contributor

mhegazy commented Oct 5, 2017

@ahejlsberg last time we talked about disabling this code path for --strictFunctionTypes you mentioned we needed this to avoid excessive recursion for computing the variance digest, is that not an issue now?

@DanielRosenwasser
Copy link
Member

It feels strange that the following examples undergo a regression and you lose checking under strictFunctionTypes:

interface Animal {a: any}
interface Dog extends Animal {d: any}


namespace n1 {
    class Foo {
        static f1(x: Animal): Animal { throw "wat"; }
        static f2(x: Dog): Animal { throw "wat"; };
    }
    
    declare let f1: (cb: typeof Foo.f1) => void;
    declare let f2: (cb: typeof Foo.f2) => void;
    f1 = f2;
    f2 = f1; // errors outside strictFunctionTypes, not with it.
}

namespace n2 {
    type BivariantHack<Input, Output> = { foo(x: Input): Output }["foo"];
    
    declare let f1: (cb: BivariantHack<Animal, Animal>) => void;
    declare let f2: (cb: BivariantHack<Dog, Animal>) => void;
    f1 = f2;
    f2 = f1; // errors outside strictFunctionTypes, not with it.
}

At the very least, we should have tests demonstrating the behavior, but I think we can still tighten this up.

@ahejlsberg
Copy link
Member Author

@DanielRosenwasser It's because a callback parameter check (contravariant parameters, bivariant return type) sits halfway between a strict function type check (contravariant parameters, covariant return type) and a method type check (bivariant parameters, bivariant return type). I suppose we could say that a callback parameter check occurs only if the callback type isn't declared as method (which you really have to contort yourself to do, as your example demonstrates). I think it is largely immaterial though, it never really occurs in normal code.

@ahejlsberg
Copy link
Member Author

last time we talked about disabling this code path for --strictFunctionTypes you mentioned we needed this to avoid excessive recursion for computing the variance digest, is that not an issue now?

Not sure what I was talking about there, it shouldn't matter.

@DanielRosenwasser
Copy link
Member

While I think it wouldn't be super difficult to do that, we should acknowledge these cases and add them to our test suite, with and without strictFunctionTypes.

@ahejlsberg
Copy link
Member Author

@DanielRosenwasser @mhegazy With the latest commits I've reverted back to using the callback parameter code path in --strictFunctionTypes mode as well, but with a tighter check on the return type of the callback. This means that in --strictFunctionTypes mode we'll check function types loosely only when they originate in method or constructor declarations and aren't in a callback parameter position.

@ahejlsberg ahejlsberg merged commit b7e744a into master Oct 6, 2017
@ahejlsberg ahejlsberg deleted the strictCallbackParameters branch October 6, 2017 20:36
Copy link

@SlurpTheo SlurpTheo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why !callbackCheck instead of CallbackCheck.None === callbackCheck?

@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

strictFunctionTypes has different behavior with parameter types and return types
5 participants