@@ -22,15 +22,8 @@ import {
2222// Public API
2323// ----------
2424
25- /**
26- * A reference to a value `T`. Can be passed to other functions to give them
27- * mutable access to the underlying value.
28- *
29- * Conceptually, it represents a WGSL pointer.
30- */
31- export interface ref < T > {
32- readonly [ $internal ] : unknown ;
33- readonly type : 'ref' ;
25+ interface ref < T > {
26+ readonly [ $internal ] : { type : 'ref' } ;
3427
3528 /**
3629 * Derefences the reference, and gives access to the underlying value.
@@ -47,10 +40,18 @@ export interface ref<T> {
4740 $ : T ;
4841}
4942
50- type RefFn = DualFn < ( < T > ( value : T ) => ref < T > ) > & { [ $internal ] : true } ;
43+ /**
44+ * A reference to a value `T`. Can be passed to other functions to give them
45+ * mutable access to the underlying value.
46+ *
47+ * Conceptually, it represents a WGSL pointer.
48+ */
49+ export type _ref < T > = T extends object ? T & ref < T > : ref < T > ;
50+
51+ type RefFn = DualFn < ( < T > ( value : T ) => _ref < T > ) > & { [ $internal ] : true } ;
5152
52- export const ref = ( ( ) => {
53- const impl = ( < T > ( value : T ) => new refImpl ( value ) ) as unknown as RefFn ;
53+ export const _ref = ( ( ) => {
54+ const impl = ( < T > ( value : T ) => INTERNAL_createRef ( value ) ) as unknown as RefFn ;
5455
5556 setName ( impl , 'ref' ) ;
5657 impl . toString = ( ) => 'ref' ;
@@ -94,39 +95,60 @@ export const ref = (() => {
9495} ) ( ) ;
9596
9697export function isRef < T > ( value : unknown | ref < T > ) : value is ref < T > {
97- return value instanceof refImpl ;
98+ return ( value as ref < T > ) ?. [ $internal ] ?. type === 'ref' ;
9899}
99100
100101// --------------
101102// Implementation
102103// --------------
103104
104- class refImpl < T > implements ref < T > {
105- readonly [ $internal ] : true ;
106- readonly type : 'ref' ;
107- #value: T ;
105+ export function INTERNAL_createRef < T > ( value : T ) : ref < T > {
106+ const target = {
107+ [ $internal ] : { type : 'ref' } ,
108108
109- constructor ( value : T ) {
110- this [ $internal ] = true ;
111- this . type = 'ref' ;
112- this . #value = value ;
113- }
109+ get $ ( ) : T {
110+ return value ;
111+ } ,
112+
113+ set $ ( newValue : T ) {
114+ if ( newValue && typeof newValue === 'object' ) {
115+ // Setting an object means updating the properties of the original object.
116+ // e.g.: foo.$ = Boid();
117+ for ( const key of Object . keys ( newValue ) as ( keyof T ) [ ] ) {
118+ value [ key ] = newValue [ key ] ;
119+ }
120+ } else {
121+ value = newValue ;
122+ }
123+ } ,
124+ } ;
114125
115- get $ ( ) : T {
116- return this . #value as T ;
126+ if ( value === undefined || value === null ) {
127+ throw new Error ( 'Cannot create a ref from undefined or null' ) ;
117128 }
118129
119- set $ ( value : T ) {
120- if ( value && typeof value === 'object' ) {
121- // Setting an object means updating the properties of the original object.
122- // e.g.: foo.$ = Boid();
123- for ( const key of Object . keys ( value ) as ( keyof T ) [ ] ) {
124- this . #value[ key ] = value [ key ] ;
125- }
126- } else {
127- this . #value = value ;
128- }
130+ if ( typeof value === 'object' ) {
131+ return new Proxy ( target , {
132+ get ( target , prop ) {
133+ if ( prop in target ) {
134+ return target [ prop as keyof typeof target ] ;
135+ }
136+ return value [ prop as keyof T ] ;
137+ } ,
138+ set ( _target , prop , propValue ) {
139+ if ( prop === $internal ) {
140+ return false ;
141+ }
142+ if ( prop === '$' ) {
143+ console . log ( 'Setting ref value:' , propValue ) ;
144+ return Reflect . set ( target , prop , propValue ) ;
145+ }
146+ return Reflect . set ( value as object , prop , propValue ) ;
147+ } ,
148+ } ) as ref < T > ;
129149 }
150+
151+ return target as ref < T > ;
130152}
131153
132154/**
0 commit comments