From ef4bc8b4f91023afac437be9179beef350b32db3 Mon Sep 17 00:00:00 2001 From: Rodrigo Faria Date: Fri, 28 Mar 2025 15:10:32 +0000 Subject: [PATCH] feat(babel-plugin-react-compiler): support satisfies operator (#32742) Solve https://github.com/facebook/react/pull/29818 --------- Co-authored-by: Rodrigo Faria --- .../src/HIR/BuildHIR.ts | 14 ++++ .../src/HIR/HIR.ts | 14 +++- .../ReactiveScopes/CodegenReactiveFunction.ts | 15 ++-- .../type-annotation-satisfies-array.expect.md | 71 +++++++++++++++++++ .../type-annotation-satisfies-array.ts | 15 ++++ ...type-annotation-satisfies-number.expect.md | 41 +++++++++++ .../type-annotation-satisfies-number.ts | 13 ++++ 7 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-array.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-array.ts create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-number.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-number.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index 6f93ef2f3a..cba4bf93ed 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -2406,6 +2406,19 @@ function lowerExpression( kind: 'TypeCastExpression', value: lowerExpressionToTemporary(builder, expr.get('expression')), typeAnnotation: typeAnnotation.node, + typeAnnotationKind: 'cast', + type: lowerType(typeAnnotation.node), + loc: exprLoc, + }; + } + case 'TSSatisfiesExpression': { + let expr = exprPath as NodePath; + const typeAnnotation = expr.get('typeAnnotation'); + return { + kind: 'TypeCastExpression', + value: lowerExpressionToTemporary(builder, expr.get('expression')), + typeAnnotation: typeAnnotation.node, + typeAnnotationKind: 'satisfies', type: lowerType(typeAnnotation.node), loc: exprLoc, }; @@ -2417,6 +2430,7 @@ function lowerExpression( kind: 'TypeCastExpression', value: lowerExpressionToTemporary(builder, expr.get('expression')), typeAnnotation: typeAnnotation.node, + typeAnnotationKind: 'as', type: lowerType(typeAnnotation.node), loc: exprLoc, }; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts index 3a8cb89ca0..5c84cbb9fc 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts @@ -910,13 +910,21 @@ export type InstructionValue = value: Place; loc: SourceLocation; } - | { + | ({ kind: 'TypeCastExpression'; value: Place; - typeAnnotation: t.FlowType | t.TSType; type: Type; loc: SourceLocation; - } + } & ( + | { + typeAnnotation: t.FlowType; + typeAnnotationKind: 'cast'; + } + | { + typeAnnotation: t.TSType; + typeAnnotationKind: 'as' | 'satisfies'; + } + )) | JsxExpression | { kind: 'ObjectExpression'; diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index 9d41663d56..02994ef0f9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -2115,10 +2115,17 @@ function codegenInstructionValue( } case 'TypeCastExpression': { if (t.isTSType(instrValue.typeAnnotation)) { - value = t.tsAsExpression( - codegenPlaceToExpression(cx, instrValue.value), - instrValue.typeAnnotation, - ); + if (instrValue.typeAnnotationKind === 'satisfies') { + value = t.tsSatisfiesExpression( + codegenPlaceToExpression(cx, instrValue.value), + instrValue.typeAnnotation, + ); + } else { + value = t.tsAsExpression( + codegenPlaceToExpression(cx, instrValue.value), + instrValue.typeAnnotation, + ); + } } else { value = t.typeCastExpression( codegenPlaceToExpression(cx, instrValue.value), diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-array.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-array.expect.md new file mode 100644 index 0000000000..d0083f3c3e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-array.expect.md @@ -0,0 +1,71 @@ + +## Input + +```javascript +// @enableUseTypeAnnotations +function Component(props: {id: number}) { + const x = makeArray(props.id) satisfies number[]; + const y = x.at(0); + return y; +} + +function makeArray(x: T): Array { + return [x]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{id: 42}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @enableUseTypeAnnotations +function Component(props) { + const $ = _c(4); + let t0; + if ($[0] !== props.id) { + t0 = makeArray(props.id); + $[0] = props.id; + $[1] = t0; + } else { + t0 = $[1]; + } + const x = t0 satisfies number[]; + let t1; + if ($[2] !== x) { + t1 = x.at(0); + $[2] = x; + $[3] = t1; + } else { + t1 = $[3]; + } + const y = t1; + return y; +} + +function makeArray(x) { + const $ = _c(2); + let t0; + if ($[0] !== x) { + t0 = [x]; + $[0] = x; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ id: 42 }], +}; + +``` + +### Eval output +(kind: ok) 42 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-array.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-array.ts new file mode 100644 index 0000000000..5024055c10 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-array.ts @@ -0,0 +1,15 @@ +// @enableUseTypeAnnotations +function Component(props: {id: number}) { + const x = makeArray(props.id) satisfies number[]; + const y = x.at(0); + return y; +} + +function makeArray(x: T): Array { + return [x]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{id: 42}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-number.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-number.expect.md new file mode 100644 index 0000000000..0819456dc1 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-number.expect.md @@ -0,0 +1,41 @@ + +## Input + +```javascript +// @enableUseTypeAnnotations +import {identity} from 'shared-runtime'; + +function Component(props: {id: number}) { + const x = identity(props.id); + const y = x satisfies number; + return y; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{id: 42}], +}; + +``` + +## Code + +```javascript +// @enableUseTypeAnnotations +import { identity } from "shared-runtime"; + +function Component(props) { + const x = identity(props.id); + const y = x satisfies number; + return y; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ id: 42 }], +}; + +``` + +### Eval output +(kind: ok) 42 \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-number.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-number.ts new file mode 100644 index 0000000000..2f4ea6222e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/type-annotations/type-annotation-satisfies-number.ts @@ -0,0 +1,13 @@ +// @enableUseTypeAnnotations +import {identity} from 'shared-runtime'; + +function Component(props: {id: number}) { + const x = identity(props.id); + const y = x satisfies number; + return y; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{id: 42}], +};