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

Dart chooses widest possible interpretation of type parameter when inferring from function arguments #3167

Closed
roblframpton opened this issue Jun 27, 2023 · 2 comments
Labels
request Requests to resolve a particular developer problem

Comments

@roblframpton
Copy link

roblframpton commented Jun 27, 2023

Consider this function:

T myFunc<T>(T input1, T input2) {
    return input1;
}

If I call this function like so:

final x = myFunc(123, 234);

then T is int, but if I call it like this:

final x = myFunc(123, "hello");

then T is Object. I can see why - it's the nearest possible supertype of both int and String. Unfortunately for my case, this is not what I wanted. I was hoping to restrict my fuction so that if the first argument was an int, the second argument is also an int.

I can see that Dart's behaviour here is not in any way wrong, but I'm curious to know why this choice was made, and whether there is a way to change the behaviour to get the effect I wanted.

Just by way of comparison, TypeScript does behave the way I had hoped:

function myFunc<T>(arg1: T, arg2: T): T {
  return arg1;
}

const z = myFunc(1 as number, ""); // Error: Argument of type 'string' is not assignable to parameter of type 'number'

class A {f1(){};}
class B {f2(){};}

const a = myFunc(new A(), new B()); // Error: Argument of type 'B' is not assignable to parameter of type 'A'.

What is Dart's justification for its behaviour, and are there situations where this is advantageous? Can I force it to behave otherwise?

@roblframpton roblframpton added the request Requests to resolve a particular developer problem label Jun 27, 2023
@Mike278
Copy link

Mike278 commented Jun 27, 2023

Looks similar to #3156

I was hoping to restrict my fuction so that if the first argument was an int, the second argument is also an int.
Can I force it to behave otherwise?

If you want the two arguments to be exactly the same type, you'll have to emulate invariance as described in #3156 (comment).

But depending on your use case, adding a subclass constraint might also be satisfactory:

A myFunc<A, B extends A>(A input1, B input2) {
    return input1;
}

Now when you write

final x = myFunc(123, "hello");

you get

Couldn't infer type parameter 'B'.

Tried to infer 'String' for 'B' which doesn't work:
  Type parameter 'B' is declared to extend 'A' producing 'int'.
The type 'String' was inferred from:
  Parameter 'input2' declared as     'B'
                     but argument is 'String'.

Consider passing explicit type argument(s) to the generic.

@roblframpton
Copy link
Author

Yes, that's exactly what I hoped for. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
request Requests to resolve a particular developer problem
Projects
None yet
Development

No branches or pull requests

2 participants