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

Prefer non-any overloads when one is available #25007

Closed
3 of 4 tasks
mpawelski opened this issue Jun 15, 2018 · 3 comments
Closed
3 of 4 tasks

Prefer non-any overloads when one is available #25007

mpawelski opened this issue Jun 15, 2018 · 3 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@mpawelski
Copy link

Use Cases

Many times you use external/3rd party definition files where one interface contains method that accepts or returns any type.
You know that in your codebase you could provide more specific overload that would help provide useful typings for you code. The problem is overload from external definitions file is always chosen first. The only way to augment this interface is to edit external definition file.

Suggestion

Choose overload with more specific (non-any) parameters when there are two overloads, one with any type and other with concrete or generic type.

Examples

These examples show that order in which interfaces are added matter. These are in one file but in real-life code the any variant is in external definition file.

interface Get3 {
  (arg: any): any;
}
interface Get3 {
  (arg: string): string;
}

var get3!: Get3;
var g3 = get3('test');
g3.wrongMethod(''); //error

//-----------//

interface Get4 {
  (arg: string): string;
}
interface Get4 {
  (arg: any): any;
}

var get4!: Get4;
var g4 = get4('test');
g4.wrongMethod(''); //no error (because any)

Expected result:
Error in both cases because non-any overload was taken first

Other example:

//---------------//

interface Bar1 {
  <T>(arg: T): T;
}
interface Bar1 {
  (arg: any): any;
}

var bar1!: Bar1;
var ret1 = bar1('a');
ret1.wrongMethod('a'); //no error (because any)
 
//-------//
interface Bar2 {
  (arg: any): any;
}
interface Bar2 {
  <T>(arg: T): T;
}

var bar2!: Bar2;
var ret2 = bar2('a');
ret2.wrongMethod('a'); //error (because choosed generic overload)

Expected result:
Error in both cases because non-any overload was taken first

Actually in the cases I shown I thought that when you first declare interface with any then this overload will be taken, just like here:

interface Bar3 {
  (arg: any): any;
  <T>(arg: T): T;
}
var bar3!: Bar3;
var ret3 = bar3('a');
ret3.wrongMethod('a'); //no error

interface Bar4 {
  <T>(arg: T): T;
  (arg: any): any;
}
var bar4!: Bar4;
var ret4 = bar4('a');
ret4.wrongMethod('a'); //error

But the whole point is: The order here should not matter for these cases. TS compiler should always pick more specific overload.

It works for literal types

Actually I read some issue (like #1860) where it was described that order of overloads matter. But I noticed that overloads with literal types are always chosen even when there is matching any overload:

interface Get1 {
  (arg: any): any;
}
interface Get1 {
  (arg: 'test'): { test: string };
}

var get1!: Get1;
var g1 = get1('test');
g1.wrongMethod(''); //error

//-----------//

interface Get2 {
  (arg: 'test'): { test: string };
}
interface Get2 {
  (arg: any): any;
}

var get2!: Get2;
var g2 = get2('test');
g2.wrongMethod(''); //error

Can't we have similar resolution in "any" vs "non-any" conflicts like in "any" vs "string literal" types? It really would be helpful for external types augmentation scenarios.

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript / JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. new expression-level syntax)
@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jun 15, 2018
@RyanCavanaugh
Copy link
Member

We're very committed to simple overload resolution - we've experimented with other options and they all had downsides we don't like. In particular, C#'s overly complex overload resolution algorithm is a constant source of pain for them. The string literal choosing thing is bad and we'd prefer to get rid of it instead of spreading that behavior further.

@DanielRosenwasser
Copy link
Member

We actually already have a weird pass that avoids any oriented overloads. We do a pass that tries to use subtyping over assignability first.

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants