ReadonlyArray.some does not perform type narrowing
See original GitHub issueBug 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:
- Created 2 years ago
- Comments:5 (3 by maintainers)
Top 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 >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I know what
some()
is but I don’t know whatany()
is. Is that from a different programming language? Maybe you want to edit the title?I don’t think you want
anyRight
to returnsideValueArray is ReadonlyArray<Right<T>>
, which implies that atrue
result means that all the elements ofsideValueArray
areRight<T>
. For all we know it could just be the first element that’s aRight<T>
and then the compiler will have unsafely assumed that all the rest of the elements areRight<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 havesome<S extends T>(pred: (val: T)=>value is not S): this is not readonly S[]
. If that were possible thenanyRight
could returnsideValueArray is not ReadonlyArray<Left<T>>
and maybe afalse
result would narrowvalues
toReadonlyArray<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-grainedelse
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()
andallLeft()
instead ofisRight()
andanyRight()
, like this (Playground link). That puts the narrowing behavior on thetrue
branch instead of thefalse
branch which is easier to represent.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 whyallLeft
works.The narrowing of
Array<A | B>
toArray<B>
by excludingA
is sound for arrays because of what arrays do, but isn’t universally valid for an arbitraryT<U | V>
.