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

Extended type parameters don't work as constraints with overloads #7378

Closed
falsandtru opened this issue Mar 3, 2016 · 4 comments
Closed
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds

Comments

@falsandtru
Copy link
Contributor

TypeScript Version:

master

Code

interface I {
  toString(): number;
}
function f<T extends number>(p: void) // should be an error
function f<T extends number>(p: number)
function f<T extends number>(p: string) // should be an error
function f<T extends number>(p: I) // should be an error
function f<T extends number>(p: T) { }
@falsandtru falsandtru changed the title Extended type parameters don't work as constrain with overloads Extended type parameters don't work as constraints with overloads Mar 3, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Mar 3, 2016

sounds like a fair check to add.

@mhegazy mhegazy added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Mar 3, 2016
@DanielRosenwasser
Copy link
Member

I think that this would be a good change, but it would make implementation/overload assignability diverge further from standard assignability rules on signatures.

From 3.11.4 in the spec:

[A type] S is _assignable to_ a type T, and T is _assignable from_ S, if S has no excess properties with respect to T (3.11.5) and one of the following is true:

  • S is an object type, an intersection type, an enum type, or the Number, Boolean, or String primitive type, T is an object type, and for each member M in T, one of the following is true:
    • M is a non-specialized call or construct signature and S has an apparent call or construct signature N where, when M and N are instantiated using type Any as the type argument for all type parameters declared by M and N (if any),

The important part is "are instantiated using type Any as the type argument for all type parameters". We look for the "erased" versions of each function.

This is not to say we couldn't change the current behavior. I'd be curious to see how code is affected, but it would be a breaking change.

@DanielRosenwasser DanielRosenwasser added the Breaking Change Would introduce errors in existing code label Mar 3, 2016
@falsandtru
Copy link
Contributor Author

similar case:

interface I {
  toString(): number;
}
function f(p: void) // erorr
function f(p: number)
function f(p: string) // erorr
function f(p: I) // erorr
function f(p: number) { }

I expect a behavior that has consistency.

function f<T extends number>(p: T) { }
f(''); // error
interface I {
  toString(): number;
}
function f<T extends number>(p: void)
function f<T extends number>(p: number)
function f<T extends number>(p: string)
function f<T extends number>(p: I)
function f<T extends number>(p: T) { }
f(''); // ok

Overloads shouldn't widen a range of type parameters.

Additionally, this behavior omits a feature of non-nullable types.

@RyanCavanaugh RyanCavanaugh added Too Complex An issue which adding support for may be too complex for the value it adds and removed Breaking Change Would introduce errors in existing code In Discussion Not yet reached consensus labels Jun 9, 2016
@RyanCavanaugh
Copy link
Member

This somewhere between too complex and "not actually wrong". For example:

function f<T extends number>(p: void)
function f<T extends number>(p: number)
function f<T extends number>(p: string) // <--
function f<T extends number>(p: T) { }

In the indicated line, T might be the type string & number, which actually satisfies the constraint. There's also no relation between all the Ts here so it's not clear what the signature is trying to say -- when T doesn't appear in the parameter list or the return type, it's totally meaningless.

The real purpose of the overload checking is to stop people from doing this (which they still manage to do with sad regularity):

function fn(x: string): void;
function fn(x: number) { }
fn(42); // Not OK, wat??

Outside of detecting the "I thought I had two signatures" case, we generally consider you to be "on your own" in terms of writing an implementation signature that is plausibly correct.

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds
Projects
None yet
Development

No branches or pull requests

4 participants