Repository navigation
Type guards in Array.prototype.filter #7657
Description
Activity
- addedSuggestionAn idea for TypeScriptAn idea for TypeScriptNeeds ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.This issue needs a plan that clarifies the finer details of how it could be implemented.In DiscussionNot yet reached consensusNot yet reached consensusDomain: lib.d.tsThe issue relates to the different libraries shipped with TypeScriptThe issue relates to the different libraries shipped with TypeScriptand removedNeeds ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.This issue needs a plan that clarifies the finer details of how it could be implemented.
on Mar 23, 2016 RyanCavanaugh commented
on Mar 23, 2016 MemberMore actionsThe required overload is
interface Array<T> { filter<U extends T>(pred: (a: T) => a is U): U[]; }
(which you can add to your codebase today to make this work)
Reacted by Sean Vieira, Yuki Kokubun, Aluan Haddad, SlurpTheo, Florian Reuschel, Panayot Cankov, Yegor Roganov, Aron Adler, Van den Berghe Jo, Mathieu Lorber and 7 moreWow, I didn't realize it would be that easy. It doesn't fix the first case (presumably because the function is not getting recognized as a type guard function), but it does work with a cast:
let numbers: number[] = [1, '2', 3, '4'] .filter(<(x) => x is number>(x => typeof x == 'number'));
Would inferring that
x => typeof x == 'number'is a type guard be a good candidate for a separate suggestion? Or is it too specific? (I didn't see anything in the tracker already).DanielRosenwasser commented
on Mar 24, 2016 MemberMore actionsThe problem I ran into with adding the overload into
lib.d.tswas that it doesn't work withArray.isArray, and I don't entirely recall why. We could investigate this a bit.Looks to be the same as #2835
RyanCavanaugh commented
on Mar 28, 2016 MemberMore actions(context: #2835 was long before we had type guards)
if someone can patch this to lib.d.ts, i could get rid of so many asserts in my code.
Reacted by Michael Messer and Aron Adler- addedHelp WantedYou can do thisYou can do thisand removedIn DiscussionNot yet reached consensusNot yet reached consensus
on Apr 11, 2016 - added this to the This milestone has been deleted milestone
on Apr 11, 2016 Is there some place to read up about the "is" in Ryan Cavanaugh (@RyanCavanaugh)'s overloading code above? Couldn't find it anywhere, neither in this issue about reserved keywords nor on the pages linked from there.
Also Google wasn't helpful this time.30 remaining items
I have tested this with TS 2.1.1.
While ReadOnlyArray.filter with type guard functions work as expected, it seems it does not work with Array.filter.const roArr: ReadonlyArray<string | null> = ["foo", null]; const fRoArr = roArr.filter((i): i is string => typeof i === "string"); // -> fRoArr inferred as string[] const arr: Array<string | null> = ["foo", null]; const fArr = arr.filter((i): i is string => typeof i === "string"); // -> fArr inferred as (string | null)[]
OliverJAsh commented
on Nov 25, 2016 ContributorMore actionsHow can you use type predicates to negate a type? For example:
[1, 'foo', true].filter((x): x is not number => typeof x !== 'number')
Not ideal, but this works for primitives. Although, it might be easier to leave that unguarded.
type NotNumber = Object | string | symbol | undefined | null | boolean | Function; function notNumber(x: any): x is NotNumber { return typeof x !== 'number'; }
Any ideas where discriminated unions will/can help here, or do we have to provide user defined type guards?
Mohamed Hegazy (@mhegazy) Ryan Cavanaugh (@RyanCavanaugh) Possible for one of you guys to chime in here? It's a lot of friction to have to (a) explicitly parametrize or (b) explicitly type guard every
.filtercall.Reacted by Sebastian NemethI tried the type guard as follows with no luck:
type FooBar = Foo | Bar interface Foo { type: 'Foo' } interface Bar { type: 'Bar' } const fooBars: Array<FooBar> = [ { type: 'Foo' }, { type: 'Bar' }, ] const foos: Array<Foo> = fooBars.filter( (item): item is Foo => item.type === 'Foo' )
Reacted by Matt Arau, William Wolf, Aliaksei Tuzik and Jonas Kugelmannmvestergaard commented
on Apr 20, 2017 More actionsIs this related to this problem? Make sure to enable strictNullChecks.
I would expect the control flow here to know that in the .map, the value is number and not undefined.
I find that this problem forces me to write much less readable code, because I have to jump through hoops to get around the type system tripping up.If this is the same issue, is there any plans to make this scenario work in the near-ish future?
Reacted by Sverre Johansen and Matt BThis issue has been fixed by #16223 and can be closed. You can try it out in
typescript@2.5.0-dev.20170712for example.Reacted by Leo Rudberg, kgtkr, Caleb Eggensperger, Thomas Hasner and Tingan HoMichael Maier (@maiermic): Are you sure? I still get an error on this code:
['a', undefined].filter((e): e is string => !!e)[0].substring(0, 10);
test.ts(1,1): error TS2532: Object is possibly 'undefined'.Playground (be sure to check "strictNullChecks" in options to see the error)
Caleb Eggensperger (@calebegg) That looks like a bug to me. Your example works if you use a function declaration instead of the lambda:
['a', undefined].filter(isString)[0].substring(0, 10); function isString(e): e is string { return !!e }
Caleb Eggensperger (@calebegg) I opened #18562 ... in the meantime, specifying the type of e explicitly is a workaround:
['a', undefined].filter((e: any): e is string => !!e)[0].substring(0, 10); ^^^^^
Reacted by Chaz Gatian and Wesley TsaiAdrian Leonhard (@NaridaL) Interesting, thanks for identifying the issue!
- removed this from the This milestone has been deleted milestone
on Apr 26, 2018 - locked and limited conversation to collaborators
on Jul 31, 2018
TypeScript Version:
nightly (1.9.0-dev.20160323)
Code
Playground
Expected behavior:
.filter with a type guard returns an array of the specified type. This would be especially useful with
--strictNullChecks, so we could do something likefoo.map(maybeReturnsNull).filter(x => x != null)....Actual behavior:
.filter returns an array of the same type, and the two
lets above are errors.