question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

ReadonlyArray.some does not perform type narrowing

See original GitHub issue

Bug Report

🔎 Search Terms

type narrowing, array, Array.some

🕗 Version & Regression Information

seems to be every version of typescript shows this behavior

⏯ Playground Link

Playground link with relevant code

💻 Code

I was writing some kind of type narrowing/validating code with Arrays, and found out that array.some could not narrow types!

I embedded playground code with comments and also pasted it down here. P.S. I already saw #14963 and it says its already fixed with ReadonlyArray, but not worked for me

function isRight<T>(sideValue:Side<T>):sideValue is Right<T>{
  return sideValue.sideType==='Right'
}

function anyRight<T>(sideValueArray:ReadonlyArray<Side<T>>):sideValueArray is ReadonlyArray<Right<T>>{
  return sideValueArray.some(isRight)
}

function usage(){
  const R3:Right<number> = {sideType:"Right"}
  const L5:Left<number> = {sideType:"Left",value:5}
  const R7:Right<number> = {sideType:"Right"}

  //declare values as ReadonlyArray in order to prevent modify memebers
  const values:ReadonlyArray<Side<number>> = [R3,L5,R7]
  //early return in case of any of them is Right
  if(anyRight(values)) return null

  //all members in "values" should be Left<number> here,
  //but typescript cannot filter out possibilities of member of values could be Right<number>
  return values.map(x=>x.value)
}

🙁 Actual behavior

ReadonlyArray.any | ReadonlyArray.some could not filter out specific types with given typeguard functions

🙂 Expected behavior

ReadonlyArray.any | ReadonlyArray.some should filter out specific types with given typeguard functions

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

4reactions
jcalzcommented, Jan 24, 2022

I know what some() is but I don’t know what any() is. Is that from a different programming language? Maybe you want to edit the title?


I don’t think you want anyRight to return sideValueArray is ReadonlyArray<Right<T>>, which implies that a true result means that all the elements of sideValueArray are Right<T>. For all we know it could just be the first element that’s a Right<T> and then the compiler will have unsafely assumed that all the rest of the elements are Right<T>. So the current return type is a problem. But there’s no way to write it correctly in the language.

There are unfortunately no negated types in TypeScript, so there’s no representation of the sort of narrowing that happens primarily when a type guard function returns false. It would be nice if you could have some<S extends T>(pred: (val: T)=>value is not S): this is not readonly S[]. If that were possible then anyRight could return sideValueArray is not ReadonlyArray<Left<T>> and maybe a false result would narrow values to ReadonlyArray<Left<number>> as desired. But it’s not possible so we can’t do this.

That means the main problem here isn’t some bug in some(), but lack of language support for negated types or perhaps a “fine-grained else type guard” as requested in #15048.


Note that in this case you’re fighting against the language. Assuming you want to make progress before waiting for TypeScript to change, you will be much happier if you refactor to isLeft() and allLeft() instead of isRight() and anyRight(), like this (Playground link). That puts the narrowing behavior on the true branch instead of the false branch which is easier to represent.

1reaction
RyanCavanaughcommented, Jan 24, 2022

To supplement @jcalz 's correct response, the key thing is that user-defined type guards (UDTGs) can narrow unions (including in the false branch), but Array<Side<T>> is not a union type - it’s a type containing a union, which is a different beast. UDTGs can also narrow via intersection, which is why allLeft works.

The narrowing of Array<A | B> to Array<B> by excluding A is sound for arrays because of what arrays do, but isn’t universally valid for an arbitrary T<U | V>.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Typescript type narrowing - Stack Overflow
I would do something like EnsureArray<T extends React.Component>(inputs: ReadonlyArray<T>): ReadonlyArray<T>; EnsureArray<T extends React.
Read more >
Documentation - TypeScript 3.4
The ReadonlyArray type describes Array s that can only be read from. Any variable with a reference to a ReadonlyArray can't add, remove,...
Read more >
Tagged Unions and Type Guards in TypeScript | by Greg Pabian
filter is still the original type instead of the planned narrowed one. It appears that TypeScript doesn't infer anything from the filter function's...
Read more >
Narrowing Library | Academy - Lucas Paganini
Now, regarding who should install it, I see this library as "Lodash for TypeScript". It provides you with flexible and type safe utilities...
Read more >
typescript-cheatsheet - GitHub Pages
In TypeScript any argument of a function may be assigned a type no matter how ... the type never when narrowed by any...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found