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

type guarded type not preserved in returned object method? #10774

Closed
benmosher opened this issue Sep 8, 2016 · 3 comments
Closed

type guarded type not preserved in returned object method? #10774

benmosher opened this issue Sep 8, 2016 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@benmosher
Copy link
Contributor

TypeScript Version: 2.0.2

Code

// a function to take either an iterator or an iterable, and return an iterable (for for..of)
export function iterable<T>(iter: Iterator<T> | Iterable<T>): Iterable<T> {
  if (isIterator(iter)) {
    //const iter2 = iter //<== returning iter2 instead works fine, iter2 is inferred to be Iterator<T>
    return {
      [Symbol.iterator]() { return iter } // <== this is marked as an error, reports iter as `Iterator<T> | Iterable<T>` in spite of the guard
    }
  }
  if (isIterable(iter)) return iter  // works fine

  throw new Error("provided argument neither iterable nor iterator")
}

// guards
export function isIterable<T>(iter: Iterable<T> | Iterator<T>): iter is Iterable<T>;
export function isIterable(iter: any): iter is Iterable<any> {
  if (iter == null) return false
  if (!(iter[Symbol.iterator] instanceof Function)) return false

  return true
}

export function isIterator<T>(iter: Iterable<T> | Iterator<T>): iter is Iterator<T>;
export function isIterator(iter: any): iter is Iterator<any> {
  if (iter == null) return false
  return (typeof iter.next === 'function')
}

Possibly related: #10734 ?

Expected behavior:

I would expect iter to be allowed as a return value, since it has been guarded.

Actual behavior:

Only iter2 is allowed to be returned, if defined.

I wondered if this might have been due to iter being a var-scoped name, so I tried defining a const iter2 = iter at the top and then calling the guards on iter2, but that also didn't work.

Apologies for the terrible title, I'm not familiar enough with typing jargon yet to succinctly describe what I'm seeing. (and I'm willing to believe it's working as intended, but I'm not positive)

@benmosher
Copy link
Contributor Author

Interesting twist--this compiles without errors:

export function iterable<T>(iter: Iterator<T> | Iterable<T>): Iterable<T> {
  if (isIterator(iter)) return { [Symbol.iterator]: () => iter }
  // this too:   if (isIterator(iter)) return { [Symbol.iterator]: function () { return iter } }
  if (isIterable(iter)) return iter

  throw new Error("provided argument neither iterable nor iterator")
}

The arrow/function expressions seems to infer the guarded type, even though the short-format method did not.

@jods4
Copy link

jods4 commented Sep 8, 2016

I think this is a duplicate of #10501, where guards flow through function and lambdas, but not through method shorthand in literals.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Sep 8, 2016
@RyanCavanaugh
Copy link
Member

Thanks for finding that @jods4

@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
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants