Skip to content

fix: narrow filterNotNull() and filterValuesNotNull() return types#18

Merged
delacry merged 1 commit into
noctud:0.1.xfrom
nikophil:fix/filter-not-null-narrowing
Jul 18, 2026
Merged

fix: narrow filterNotNull() and filterValuesNotNull() return types#18
delacry merged 1 commit into
noctud:0.1.xfrom
nikophil:fix/filter-not-null-narrowing

Conversation

@nikophil

Copy link
Copy Markdown
Contributor

Hi! I just gave a try to the solution you suggested to remove null from the type

fixes #16

Use a conditional return type so that filtering out nulls also removes
null from the element type: ImmutableList<int|null>::filterNotNull()
is now typed ImmutableList<int>, and Map<K, V|null>::filterValuesNotNull()
is now typed Map<K, V>.

Fixes noctud#16
@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

βœ… All modified and coverable lines are covered by tests.

πŸ“’ Thoughts on this report? Let us know!

@delacry

delacry commented Jul 18, 2026

Copy link
Copy Markdown
Member

Thank you very much! I was just thinking about it, because I said I'll look at it yesterday (but I haven't found the time), this looks perfect, I'll merge it and tag the next version right away.

@delacry
delacry merged commit fb1ab79 into noctud:0.1.x Jul 18, 2026
6 checks passed
@nikophil

nikophil commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

@delacry LOL while rebasing #19, I asked Claude to spread tests in tests/Type/FilterNotNullTypeTest.php into the other files, and it just said:

By the way, nice fix with (E is null ? never : E) β€” it's cleaner than what I had in mind (I thought type subtraction wasn't expressible, but the conditional distributing over the union does exactly the job).

so.... congrats ? πŸ˜…

BTW, there's a feature request for being able to substract types in PHPStan phpstan/phpstan#3182

@delacry

delacry commented Jul 18, 2026

Copy link
Copy Markdown
Member

so.... congrats ? πŸ˜…

Yeeah.. so I'm going to pass that congrats to my Fable 5 with max effort πŸ˜„ I told it to have crack at it, I just suspected that it might be possible.

It's interesting how the AI is progressing, because when I started to write the library in the summer of 2025, it was no where near the level as it is now, I described whole library in detail in one big MD file and I hoped AI would do most of the work, but when I saw the result I just deleted it right away and wrote it by hand from scratch, and only like 4 months after there were solid foundation I started using AI for some verifications, fixing docblocks and adding some methods that I missed, and writing some tests I wouldn't otherwise wrote.

And I remember solving this filterNotNull problem with AI before I was about to release the library, and it just couldn't crack it and it said it's just not possible because there's no such thing like subtraction, but when I discussed it with Fable few days ago, it knew right away that conditional returns are evaluated separately for each member and then the result is put together (which I had no idea that it works like this), and I think it said it knew it because TypeScript does the same thing.. and I was like wait, so it was this easy the whole time.. πŸ€¦β€β™‚οΈ

@nikophil

Copy link
Copy Markdown
Contributor Author

yeah I also feel like 2026 is the game changing year with AI

I'm pretty sure you'd give your .md now to Fable 5, the result would be much more acceptable

delacry added a commit that referenced this pull request Jul 25, 2026
Update the API reference for narrowing that landed in #15, #17, #18,
#19, #21 and #22: flatten(), filterNotNull(),
Map::filterValuesNotNull(), countWhere(), Collection::sum() and
Map::toArray().

Compact types are inlined in the signature (countWhere(): int<0, max>);
conditional ones are described in prose instead, matching how sum() and
intersect() are already documented.

Also fix the flatten() assertion in TransformTypeTest, which still
expected Collection<mixed> and has failed on 0.1.x since #21.
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

Successfully merging this pull request may close these issues.

filterNotNull() does not remove null from type

2 participants