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

The .filter(Boolean) doesn't seem to be working as expected #146

Closed
u3u opened this issue Jun 2, 2023 · 4 comments
Closed

The .filter(Boolean) doesn't seem to be working as expected #146

u3u opened this issue Jun 2, 2023 · 4 comments

Comments

@u3u
Copy link

u3u commented Jun 2, 2023

// Actual: `(number | true)[]`
// Expectation: `number[]`
const numbers = [1, 0, false, null, undefined].filter(Boolean)

TS Playground

@JakeTrevor
Copy link

JakeTrevor commented Jun 2, 2023

True won't get filtered out by this (resulting in an array [1, true]) so the type signature is actually correct - not quite sure what you mean @u3u.

There is some weirdness with .filter I have encountered, but it's to do with 2D-arrays; here's an example:

function makeRows({
  row1,
  row2,
  row3,
  row4,
  row5,
}: {
  row1?: number[];
  row2?: number[];
  row3?: number[];
  row4?: number[];
  row5?: number[];
}) {
  let rows = [row1, row2, row3, row4, row5];
  rows = rows.filter(Boolean);
  //^? let rows: (number[] | undefined)[]
 // But I expect number[][]
}

Am I missing something?

EDIT:
I solved my own problem - the solution is to do this instead:

let rows = [row1, row2, row3, row4, row5].filter(Boolean);
//    ^? let rows:  number[][]

I guess because the type inference carries down from the initial delcaration

@u3u
Copy link
Author

u3u commented Jun 2, 2023

@JakeTrevor Sorry, the example I gave may have some problems.
In fact, it is the following code that still infers as (number | true)[], but the actual result should be number[].

const numbers = [1, 0, false, null, undefined].filter(Boolean)

@JakeTrevor
Copy link

JakeTrevor commented Jun 20, 2023

Weird as it is, the behaviour actually is correct; Take a look at this screen cap:
image
As you can see, the type of the array you are filtering includes a bunch of falsey values, numbers and booleans. All the falsey values will get filtered out, resulting in numbers and booleans. But non-falsey booleans are of course all true - so we get a final type of number | true.

In this context, typescript knows nothing about the contents of the array - all it knows is that it contains some number of booleans. So all it can assert is that the resulting filtered array may contains numbers or true.

If you tell typescript the array contains only false instances, not booleans, it does get it right. see below:
image

However for very good reasons, typescript does not assume this, instead widening the type to boolean, instead of just false. You can also get it to work by casting the array as const:
image

In this case you are telling typescript that the contents of the array will only ever be what you describe in this literal, and so it can make even further inferences.

However this should this should certainly not happen by default - and if you need more specificity, then you can manually mark the type as I have done above. I think the existing behaviour is quite sensible so this issue should probaly be closed.

@mattpocock
Copy link
Collaborator

Closing, @JakeTrevor is correct.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants