Skip to content

Commit

Permalink
[compiler] Type ref prop as a ref
Browse files Browse the repository at this point in the history
Adds a shape type for component props, which has one defined property: "ref". This means that if the ref property exists, we can type usage of `props.ref` (or via destructuring) the same as the result of `useRef()` and infer downstream usage similarly.

ghstack-source-id: 76cd07c5dfeea2a4aafe141912663b097308fd73
Pull Request resolved: #29834
  • Loading branch information
josephsavona committed Jun 10, 2024
1 parent bf1bb2e commit c50d593
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export type ObjectShape = {
* the inferred types for [] and {}.
*/
export type ShapeRegistry = Map<string, ObjectShape>;
export const BuiltInPropsId = "BuiltInProps";
export const BuiltInArrayId = "BuiltInArray";
export const BuiltInFunctionId = "BuiltInFunction";
export const BuiltInJsxId = "BuiltInJsx";
Expand All @@ -207,6 +208,11 @@ export const BuiltInDispatchId = "BuiltInDispatch";
// ShapeRegistry with default definitions for built-ins.
export const BUILTIN_SHAPES: ShapeRegistry = new Map();

// If the `ref` prop exists, it has the ref type
addObject(BUILTIN_SHAPES, BuiltInPropsId, [
["ref", { kind: "Object", shapeId: BuiltInUseRefId }],
]);

/* Built-in array shape */
addObject(BUILTIN_SHAPES, BuiltInArrayId, [
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
BuiltInFunctionId,
BuiltInJsxId,
BuiltInObjectId,
BuiltInPropsId,
BuiltInUseRefId,
} from "../HIR/ObjectShape";
import { eachInstructionLValue, eachInstructionOperand } from "../HIR/visitors";
Expand Down Expand Up @@ -101,7 +102,13 @@ function* generate(
func: HIRFunction
): Generator<TypeEquation, void, undefined> {
if (func.env.fnType === "Component") {
const [_, ref] = func.params;
const [props, ref] = func.params;
if (props && props.kind === "Identifier") {
yield equation(props.identifier.type, {
kind: "Object",
shapeId: BuiltInPropsId,
});
}
if (ref && ref.kind === "Identifier") {
yield equation(ref.identifier.type, {
kind: "Object",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

## Input

```javascript
// @validateRefAccessDuringRender @compilationMode(infer)
function Component({ ref }) {
const value = ref.current;
return <div>{value}</div>;
}

```


## Error

```
2 | function Component({ ref }) {
3 | const value = ref.current;
> 4 | return <div>{value}</div>;
| ^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at read $17:TObject<BuiltInRefValue> (4:4)
5 | }
6 |
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// @validateRefAccessDuringRender @compilationMode(infer)
function Component({ ref }) {
const value = ref.current;
return <div>{value}</div>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

## Input

```javascript
// @validateRefAccessDuringRender @compilationMode(infer)
function Component(props) {
const value = props.ref.current;
return <div>{value}</div>;
}

```


## Error

```
2 | function Component(props) {
3 | const value = props.ref.current;
> 4 | return <div>{value}</div>;
| ^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at read $15:TObject<BuiltInRefValue> (4:4)
5 | }
6 |
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// @validateRefAccessDuringRender @compilationMode(infer)
function Component(props) {
const value = props.ref.current;
return <div>{value}</div>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

## Input

```javascript
// @validateRefAccessDuringRender @compilationMode(infer)
function Component(props) {
const ref = props.ref;
ref.current = true;
return <div>{value}</div>;
}

```


## Error

```
2 | function Component(props) {
3 | const ref = props.ref;
> 4 | ref.current = true;
| ^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (4:4)
5 | return <div>{value}</div>;
6 | }
7 |
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @validateRefAccessDuringRender @compilationMode(infer)
function Component(props) {
const ref = props.ref;
ref.current = true;
return <div>{value}</div>;
}

0 comments on commit c50d593

Please sign in to comment.