Hey everyone! I'd like to propose a way to create proxies for primitive. I wrote a small draft here: GitHub - hugoattal/tc39-proposal-primitive-proxy: Primitive proxy proposal
Here's the essence of it:
TC39 proposal: Reference proxy
Synopsis
The proposed reference proxy is aimed at providing a mechanism for creating reference variables with user-defined set and get functions. This allows developers to add custom functionality to primitive types without having to wrap them in objects.
Prerequisite
Maybe:
Motivation
The proposed reference proxy would simplify properties of reactive frameworks such as React or Vue. These frameworks rely heavily on the ability to create reactive properties that can update automatically in response to changes in the underlying data.
There are two solutions:
- React-like reactive properties use a getter to get the variable, and a function to set it.
- Vue-like reactive properties wrap the variable into a Proxy, where the value can be set and get using the
value
property.
A reference proxy would allow setting and getting a reactive variable seamlessly, here's an example:
// React
const [myReactVar, setMyReactVar] = useState("");
setMyReactVar("Hello world");
console.log(myReactVar);
// Vue
const myVueVar = ref("");
myVueVar.value = "Hello world";
console.log(myVueVar.value);
// Primitive proxy
let myReferenceVar = makeRef("");
myReferenceVar = "Hello world";
console.log(myReferenceVar );
Possible API
Here is a possible implementation inspired by the Proxy object, but without the prop argument of the handler get
and set
.
let myVar = new ReferenceProxy({ storedValue: "" }, {
set: (target, value) => {
target.storedValue = value + " world";
},
get: (target) => {
return target.storedValue + " !";
}
});
myVar = "Hello";
console.log(myVar); // "Hello world !"
Here, storedValue
is just for example purpose. The first argument of ReferenceProxy
is an object where you can store values that can be accessed with the target
argument of set
and get
.
API details
The ReferenceProxy
constructor takes two arguments: target
and handler
. target
is an object to store values, and handler
is an object containing the methods to intercept the operations performed on the target. The handler object can have get
and set
methods, which will be called when the target is accessed or modified.
ReferenceProxy<TTarget, TPrimitive>(
target: TTarget,
handler: {
set: (target: TTarget, value: TPrimitive) => void
get: (target: TTarget) => TPrimitive
}
);
Questions
Isn't it confusing not to be able to reassign a variable?
It's the same thing as standard Proxy. Setting a value to myObject.thing
does not necessary mean that the thing
property of myObject
contains the set value, if the object is behind a Proxy.
Why using a let
instead of a const
?
For consistency, const
should not be updatable. So a const
Primitive Proxy should disable the set
handler.
How to unproxify a reference proxy?
Simply use the valueOf
function like this:
let a = new PrimitiveProxy(...);
let b = a.valueOf(); // b is a standard primitive
What happens when passing a Reference Proxy to a function?
When passed to a function, the Reference Proxy is copied just like an object.
let a = new ReferenceProxy({storedValue: ""}, {
set: (target, value) => {
target.storedValue = value;
},
get: (target) => {
return "primitive:" + target.storedValue;
}
});
a = "Hello";
console.log(a); // "primitive:Hello"
((value) => {
console.log(value); // "primitive:Hello"
value = "test";
console.log(value); // "primitive:test"
})(a);
console.log(a); // "primitive:test"
So, when is getter called?
The getter is called when the primitive value of the Reference Proxy is needed.
Here are some examples:
let a = new ReferenceProxy({storedValue: ""}, {
set: (target, value) => {
console.log("set");
target.storedValue = value;
},
get: (target) => {
console.log("get");
return target.storedValue;
}
});
a = 5 // set
console.log(a) // get
const b = a; // copy Primitive Proxy
const c = a + 5 // get
const d = 5 + a // get
const e = "Hello" + a // get
const f = (a === 5) // get
What do you guys think of this?