From 7e051b04f3f73ad3dd2f750f48f23907b5ffd2c2 Mon Sep 17 00:00:00 2001 From: Rodrigo Faria Date: Tue, 25 Mar 2025 15:44:06 +0000 Subject: [PATCH 1/6] feat(babel-plugin-react-compiler): support satisfies operator --- .../packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 6f93ef2f3a3df..64003baf57ed3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -2410,8 +2410,9 @@ function lowerExpression( loc: exprLoc, }; } + case 'TSSatisfiesExpression': case 'TSAsExpression': { - let expr = exprPath as NodePath; + let expr = exprPath as NodePath; const typeAnnotation = expr.get('typeAnnotation'); return { kind: 'TypeCastExpression', From d0dbe7f9ea77d78139cc4f6096c80006f2a445c8 Mon Sep 17 00:00:00 2001 From: Rodrigo Faria Date: Tue, 25 Mar 2025 16:00:28 +0000 Subject: [PATCH 2/6] refactor: add tests for satisfies operator --- ...type-annotation-satisfies-number.expect.md | 41 +++++++++++++++++++ .../type-annotation-satisfies-number.ts | 13 ++++++ 2 files changed, 54 insertions(+) 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/__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 0000000000000..0819456dc1bfa --- /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 0000000000000..2f4ea6222e02b --- /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}], +}; From 3227d3972576cf106eef0e7d7531cedbb6a8aa8e Mon Sep 17 00:00:00 2001 From: Rodrigo Faria Date: Tue, 25 Mar 2025 16:02:36 +0000 Subject: [PATCH 3/6] refactor: add tests for satisfies operator for arrays --- .../type-annotation-satisfies-array.expect.md | 71 +++++++++++++++++++ .../type-annotation-satisfies-array.ts | 15 ++++ 2 files changed, 86 insertions(+) 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 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 0000000000000..d0083f3c3ecd9 --- /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 0000000000000..5024055c1095c --- /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}], +}; From 472e40c2aa72f9c8f2ca256b9f1fd3c16e2bcdd4 Mon Sep 17 00:00:00 2001 From: Rodrigo Faria Date: Tue, 25 Mar 2025 17:35:58 +0000 Subject: [PATCH 4/6] chore: format code --- .../packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 64003baf57ed3..f013850fe7014 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -2412,7 +2412,9 @@ function lowerExpression( } case 'TSSatisfiesExpression': case 'TSAsExpression': { - let expr = exprPath as NodePath; + let expr = exprPath as NodePath< + t.TSAsExpression | t.TSSatisfiesExpression + >; const typeAnnotation = expr.get('typeAnnotation'); return { kind: 'TypeCastExpression', From 1c815c2742edeb389eb15646613dee556cf6cffe Mon Sep 17 00:00:00 2001 From: Rodrigo Faria Date: Thu, 27 Mar 2025 15:55:52 +0000 Subject: [PATCH 5/6] feat: add support for satisfies operator in CodegenReactiveFunction --- .../src/ReactiveScopes/CodegenReactiveFunction.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) 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 b90e4e417c6a1..1d0d2ef362ca2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -2113,10 +2113,17 @@ function codegenInstructionValue( } case 'TypeCastExpression': { if (t.isTSType(instrValue.typeAnnotation)) { - value = t.tsAsExpression( - codegenPlaceToExpression(cx, instrValue.value), - instrValue.typeAnnotation, - ); + if (t.isTSSatisfiesExpression(instrValue.typeAnnotation)) { + 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), From 29beb0529906b3afdafdf60f6cb7ed80c70e1451 Mon Sep 17 00:00:00 2001 From: Rodrigo Faria Date: Thu, 27 Mar 2025 16:47:24 +0000 Subject: [PATCH 6/6] refactor(compiler): properly support satisfies operator in type cast expressions --- .../src/HIR/BuildHIR.ts | 19 +++++++++++++++---- .../src/HIR/HIR.ts | 14 +++++++++++--- .../ReactiveScopes/CodegenReactiveFunction.ts | 2 +- 3 files changed, 27 insertions(+), 8 deletions(-) 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 f013850fe7014..cba4bf93edb65 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -2406,20 +2406,31 @@ 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, }; } - case 'TSSatisfiesExpression': case 'TSAsExpression': { - let expr = exprPath as NodePath< - t.TSAsExpression | t.TSSatisfiesExpression - >; + let expr = exprPath as NodePath; const typeAnnotation = expr.get('typeAnnotation'); return { 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 3a8cb89ca0fca..5c84cbb9fc4dd 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 1d0d2ef362ca2..95b6c1ad974e1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -2113,7 +2113,7 @@ function codegenInstructionValue( } case 'TypeCastExpression': { if (t.isTSType(instrValue.typeAnnotation)) { - if (t.isTSSatisfiesExpression(instrValue.typeAnnotation)) { + if (instrValue.typeAnnotationKind === 'satisfies') { value = t.tsSatisfiesExpression( codegenPlaceToExpression(cx, instrValue.value), instrValue.typeAnnotation,