From dc25c988283a222b8c7cb7c4af1b4d974622f498 Mon Sep 17 00:00:00 2001 From: Raashish Aggarwal <94279692+raashish1601@users.noreply.github.com> Date: Sat, 2 May 2026 10:44:07 +0530 Subject: [PATCH] Infer filter as a mixed readonly array method --- .../src/TypeInference/InferTypes.ts | 9 ++ ...local-mutation-in-filter-usememo.expect.md | 99 +++++++++++++++++++ .../local-mutation-in-filter-usememo.js | 23 +++++ 3 files changed, 131 insertions(+) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/local-mutation-in-filter-usememo.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/local-mutation-in-filter-usememo.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts index 190e3d3a7a3e..b244fd987e5b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts @@ -108,6 +108,9 @@ function equation(left: Type, right: Type): TypeEquation { }; } +// These method names only exist on arrays in the MixedReadonly model. +const ARRAY_METHODS_ON_MIXED_READONLY = new Set(['filter']); + function* generate( func: HIRFunction, ): Generator { @@ -321,6 +324,12 @@ function* generateInstructionTypes( } case 'PropertyLoad': { + if (ARRAY_METHODS_ON_MIXED_READONLY.has(String(value.property))) { + yield equation(value.object.identifier.type, { + kind: 'Object', + shapeId: BuiltInMixedReadonlyId, + }); + } yield equation(left, { kind: 'Property', objectType: value.object.identifier.type, diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/local-mutation-in-filter-usememo.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/local-mutation-in-filter-usememo.expect.md new file mode 100644 index 000000000000..dcc5c6319ed7 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/local-mutation-in-filter-usememo.expect.md @@ -0,0 +1,99 @@ + +## Input + +```javascript +import React from 'react'; + +export function Component({items}) { + const stuff = React.useMemo(() => { + let isCool = false; + const someItems = items.filter(cause => { + if (cause.foo) { + isCool = true; + } + return true; + }); + + if (someItems.length > 0) { + return {someItems, isCool}; + } + }, [items]); + return
{stuff?.someItems.length}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{items: [{foo: true}, {foo: false}]}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import React from "react"; + +export function Component(t0) { + const $ = _c(8); + const { items } = t0; + let t1; + bb0: { + let isCool; + let t2; + if ($[0] !== items) { + isCool = false; + t2 = items.filter((cause) => { + if (cause.foo) { + isCool = true; + } + + return true; + }); + $[0] = items; + $[1] = isCool; + $[2] = t2; + } else { + isCool = $[1]; + t2 = $[2]; + } + const someItems = t2; + + if (someItems.length > 0) { + let t3; + if ($[3] !== isCool || $[4] !== someItems) { + t3 = { someItems, isCool }; + $[3] = isCool; + $[4] = someItems; + $[5] = t3; + } else { + t3 = $[5]; + } + t1 = t3; + break bb0; + } + t1 = undefined; + } + const stuff = t1; + + const t2 = stuff?.someItems.length; + let t3; + if ($[6] !== t2) { + t3 =
{t2}
; + $[6] = t2; + $[7] = t3; + } else { + t3 = $[7]; + } + return t3; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ items: [{ foo: true }, { foo: false }] }], +}; + +``` + +### Eval output +(kind: ok)
2
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/local-mutation-in-filter-usememo.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/local-mutation-in-filter-usememo.js new file mode 100644 index 000000000000..d2277fb58ae8 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/local-mutation-in-filter-usememo.js @@ -0,0 +1,23 @@ +import React from 'react'; + +export function Component({items}) { + const stuff = React.useMemo(() => { + let isCool = false; + const someItems = items.filter(cause => { + if (cause.foo) { + isCool = true; + } + return true; + }); + + if (someItems.length > 0) { + return {someItems, isCool}; + } + }, [items]); + return
{stuff?.someItems.length}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{items: [{foo: true}, {foo: false}]}], +};