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

Unused type parameter should be indefered as {} #15392

Closed
Gozala opened this issue Apr 26, 2017 · 6 comments
Closed

Unused type parameter should be indefered as {} #15392

Gozala opened this issue Apr 26, 2017 · 6 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@Gozala
Copy link

Gozala commented Apr 26, 2017

TypeScript Version: 2.2.2

Code

class Task<x, a> {
  execute:(succeed: (value: a) => void, fail: (error: x) => void) => number | void

  static fail <x, a> (error: x): Task<x, a> {
      return new Fail(error)
  }

  fork(succeed: (value: a) => void, fail: (error: x) => void) {
    this.execute(succeed, fail)
  }
}

class Fail<x, a> extends Task<x, a> {
  error:x
  constructor(error: x) {
    super()
    this.error = error
  }
  fork(succeed: (value: a) => void, fail: (error: x) => void) {
    fail(this.error)
  }
}

const task:Task<number, void> = Task.fail(4) // <- Type 'Task<number, {}>' is not assignable to type 'Task<number, void>'.

Expected behavior:

I would expect Task.fail(4) to be inferred as Task<number, *> where * type is open for further inference. In other words it should be able to satisfy Task<number, void> or Task<number, number> or whatever else maybe thrown at it.

It is also worth mentioning that same code type checks in flow as expected.

Actual behavior:

Unused type parameter is inferred as {} making causing a type checker to fail on commented line with a following error:

Type Task<number, {}> is not assignable to type Task<number, void>.
Type {} is not assignable to type void.

@Gozala
Copy link
Author

Gozala commented Apr 26, 2017

It also seems that problem persists even class does not use type parameter here is a refactored example:

// @flow

class Future<x, a> {
  constructor(private execute: (succeed: (value: a) => void, fail: (error: x) => void) => number | void) {
      
  }

  fork(succeed: (value: a) => void, fail: (error: x) => void) {
    this.execute(succeed, fail)
  }
}

class Fail<x>  {
  error:x
  constructor(error: x) {
    this.error = error
  }
  fork <a> (succeed: (value: a) => void, fail: (error: x) => void) {
    fail(this.error)
  }
}

const fail = <x, a> (error: x):Task<x, a> =>
  new Fail(error)

type Task<x, a> =
    | Fail<x>
    | Future<x, a>

const task:Task<number, void> = fail(4) // <- Type 'Task<number, {}>' is not assignable to type 'Task<number, void>'.

Which still has same error:

Type 'Task<number, {}>' is not assignable to type 'Task<number, void>'.
Type 'Future<number, {}>' is not assignable to type 'Task<number, void>'.
Type 'Future<number, {}>' is not assignable to type 'Future<number, void>'.
Type '{}' is not assignable to type 'void'.

But it type checks fine if following code if Fail is instantiated directly:

const task:Task<number, void> = new Fail(4)

Presumably that's due to a type parameter in the succeed function itself.

@mhegazy
Copy link
Contributor

mhegazy commented Apr 26, 2017

If a type parameter has no inference location (and no default), then inference gives it the upper bound which is {}. the type parameter is not open for further inference at that point.

if you would want that type parameter to match anything if not specified, consider adding a default to it as fail <x, a = any> (error: x): Task<x, a>.

The problem here seems to be the same as #11152. the generic type is only referenced in return type position, which is currently not an inference position, #11152 tracks addressing that.

@mhegazy mhegazy added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Apr 26, 2017
@Gozala
Copy link
Author

Gozala commented Apr 26, 2017

@mhegazy Thanks for the clarification.

if you would want that type parameter to match anything if not specified, consider adding a default to it as fail <x, a = any> (error: x): Task<x, a>.

It's more like I presume that if a has no effect on result it should be able to match anything rather than {} (any object ? or does it has some other meaning) in TS ?

The problem here seems to be the same as #11152. the generic type is only referenced in return type position, which is currently not an inference position, #11152 tracks addressing that.

Does that mean that once #11152 is fixed both examples would work as expected ? Or would using any still be required ?

@mhegazy
Copy link
Contributor

mhegazy commented Apr 26, 2017

It's more like I presume that if a has no effect on result it should be able to match anything rather than {} (any object ? or does it has some other meaning) in TS ?

The rational for this is that a type parameter with no inference location is suspect. setting it to any allows it to slip through unnoticed, where as the {} makes it fail on usage, forcing users to specify that explicitly or fix the declaration. you can specify any manually as a default if this is the intent. i think in your case it does make sense to set it to any.

Does that mean that once #11152 is fixed both examples would work as expected ? Or would using any still be required ?

#11152 does not change the general case, unmatched type parameters will still be {}; it should address some of the common cases where you have a contextual type that is today ignored, but with #11152 would be used and would contribute to inference.

@Gozala
Copy link
Author

Gozala commented Apr 26, 2017

Also is a = any a new feature not available in 2.2.2 ? I get a error TS1005: ',' expected., but it seems to work on the playground 😕

@Gozala
Copy link
Author

Gozala commented Apr 26, 2017

Also is a = any a new feature not available in 2.2.2 ? I get a error TS1005: ',' expected., but it seems to work on the playground 😕

Never mind I found answer to my question https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#generic-parameter-defaults

@mhegazy mhegazy closed this as completed May 17, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

2 participants