@@ -27,53 +27,154 @@ export const createEndpoint = Symbol("Comlink.endpoint");
27
27
export const releaseProxy = Symbol ( "Comlink.releaseProxy" ) ;
28
28
const throwSet = new WeakSet ( ) ;
29
29
30
- // prettier-ignore
31
- type Promisify < T > =
32
- T extends { [ proxyMarker ] : boolean }
33
- ? Promise < Remote < T > >
34
- : T extends ( ...args : infer R1 ) => infer R2
35
- ? ( ...args : R1 ) => Promisify < R2 >
36
- : Promise < T > ;
30
+ /**
31
+ * Interface of values that were marked to be proxied with `comlink.proxy()`.
32
+ * Can also be implemented by classes.
33
+ */
34
+ export interface ProxyMarked {
35
+ [ proxyMarker ] : true ;
36
+ }
37
+
38
+ /**
39
+ * Takes a type and wraps it in a Promise, if it not already is one.
40
+ * This is to avoid `Promise<Promise<T>>`.
41
+ *
42
+ * This is the inverse of `Unpromisify<T>`.
43
+ */
44
+ type Promisify < T > = T extends Promise < unknown > ? T : Promise < T > ;
45
+ /**
46
+ * Takes a type that may be Promise and unwraps the Promise type.
47
+ * If `P` is not a Promise, it returns `P`.
48
+ *
49
+ * This is the inverse of `Promisify<T>`.
50
+ */
51
+ type Unpromisify < P > = P extends Promise < infer T > ? T : P ;
52
+
53
+ /**
54
+ * Takes the raw type of a remote property and returns the type that is visible to the local thread on the proxy.
55
+ *
56
+ * Note: This needs to be its own type alias, otherwise it will not distribute over unions.
57
+ * See https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types
58
+ */
59
+ type RemoteProperty < T > =
60
+ // If the value is a method, comlink will proxy it automatically.
61
+ // Objects are only proxied if they are marked to be proxied.
62
+ // Otherwise, the property is converted to a Promise that resolves the cloned value.
63
+ T extends Function | ProxyMarked ? Remote < T > : Promisify < T > ;
64
+
65
+ /**
66
+ * Takes the raw type of a property as a remote thread would see it through a proxy (e.g. when passed in as a function
67
+ * argument) and returns the type that the local thread has to supply.
68
+ *
69
+ * This is the inverse of `RemoteProperty<T>`.
70
+ *
71
+ * Note: This needs to be its own type alias, otherwise it will not distribute over unions. See
72
+ * https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types
73
+ */
74
+ type LocalProperty < T > = T extends Function | ProxyMarked
75
+ ? Local < T >
76
+ : Unpromisify < T > ;
77
+
78
+ /**
79
+ * Proxies `T` if it is a `ProxyMarked`, clones it otherwise (as handled by structured cloning and transfer handlers).
80
+ */
81
+ export type ProxyOrClone < T > = T extends ProxyMarked ? Remote < T > : T ;
82
+ /**
83
+ * Inverse of `ProxyOrClone<T>`.
84
+ */
85
+ export type UnproxyOrClone < T > = T extends RemoteObject < ProxyMarked >
86
+ ? Local < T >
87
+ : T ;
37
88
38
- // prettier-ignore
89
+ /**
90
+ * Takes the raw type of a remote object in the other thread and returns the type as it is visible to the local thread
91
+ * when proxied with `Comlink.proxy()`.
92
+ *
93
+ * This does not handle call signatures, which is handled by the more general `Remote<T>` type.
94
+ *
95
+ * @template T The raw type of a remote object as seen in the other thread.
96
+ */
97
+ export type RemoteObject < T > = { [ P in keyof T ] : RemoteProperty < T [ P ] > } ;
98
+ /**
99
+ * Takes the type of an object as a remote thread would see it through a proxy (e.g. when passed in as a function
100
+ * argument) and returns the type that the local thread has to supply.
101
+ *
102
+ * This does not handle call signatures, which is handled by the more general `Local<T>` type.
103
+ *
104
+ * This is the inverse of `RemoteObject<T>`.
105
+ *
106
+ * @template T The type of a proxied object.
107
+ */
108
+ export type LocalObject < T > = { [ P in keyof T ] : LocalProperty < T [ P ] > } ;
109
+
110
+ /**
111
+ * Additional special comlink methods available on each proxy returned by `Comlink.wrap()`.
112
+ */
113
+ export interface ProxyMethods {
114
+ [ createEndpoint ] : ( ) => Promise < MessagePort > ;
115
+ [ releaseProxy ] : ( ) => void ;
116
+ }
117
+
118
+ /**
119
+ * Takes the raw type of a remote object, function or class in the other thread and returns the type as it is visible to
120
+ * the local thread from the proxy return value of `Comlink.wrap()` or `Comlink.proxy()`.
121
+ */
39
122
export type Remote < T > =
40
- (
41
- T extends ( ...args : infer R1 ) => infer R2
42
- ? ( ...args : R1 ) => Promisify < R2 >
43
- : unknown
44
- ) &
45
- (
46
- T extends { new ( ...args : infer R1 ) : infer R2 }
47
- ? { new ( ...args : R1 ) : Promise < Remote < R2 > > }
48
- : unknown
49
- ) &
50
- (
51
- T extends Object
52
- ? { [ K in keyof T ] : Remote < T [ K ] > }
53
- : unknown
54
- ) &
55
- (
56
- T extends string
57
- ? Promise < string >
58
- : unknown
59
- ) &
60
- (
61
- T extends number
62
- ? Promise < number >
63
- : unknown
64
- ) &
65
- (
66
- T extends boolean
67
- ? Promise < boolean >
68
- : unknown
69
- ) & {
70
- [ createEndpoint ] : ( ) => Promise < MessagePort > ;
71
- [ releaseProxy ] : ( ) => void ;
72
- } ;
123
+ // Handle properties
124
+ RemoteObject < T > &
125
+ // Handle call signature (if present)
126
+ ( T extends ( ...args : infer TArguments ) => infer TReturn
127
+ ? (
128
+ ...args : { [ I in keyof TArguments ] : UnproxyOrClone < TArguments [ I ] > }
129
+ ) => Promisify < ProxyOrClone < Unpromisify < TReturn > > >
130
+ : unknown ) &
131
+ // Handle construct signature (if present)
132
+ // The return of construct signatures is always proxied (whether marked or not)
133
+ ( T extends { new ( ...args : infer TArguments ) : infer TInstance }
134
+ ? {
135
+ new (
136
+ ...args : {
137
+ [ I in keyof TArguments ] : UnproxyOrClone < TArguments [ I ] > ;
138
+ }
139
+ ) : Promisify < RemoteObject < TInstance > > ;
140
+ }
141
+ : unknown ) &
142
+ // Include additional special comlink methods available on the proxy.
143
+ ProxyMethods ;
73
144
74
- declare var x : Remote < number > ;
145
+ /**
146
+ * Expresses that a type can be either a sync or async.
147
+ */
148
+ type MaybePromise < T > = Promise < T > | T ;
75
149
76
- declare var y : PromiseLike < number > ;
150
+ /**
151
+ * Takes the raw type of a remote object, function or class as a remote thread would see it through a proxy (e.g. when
152
+ * passed in as a function argument) and returns the type the local thread has to supply.
153
+ *
154
+ * This is the inverse of `Remote<T>`. It takes a `Remote<T>` and returns its original input `T`.
155
+ */
156
+ export type Local < T > =
157
+ // Omit the special proxy methods (they don't need to be supplied, comlink adds them)
158
+ Omit < LocalObject < T > , keyof ProxyMethods > &
159
+ // Handle call signatures (if present)
160
+ ( T extends ( ...args : infer TArguments ) => infer TReturn
161
+ ? (
162
+ ...args : { [ I in keyof TArguments ] : ProxyOrClone < TArguments [ I ] > }
163
+ ) => // The raw function could either be sync or async, but is always proxied automatically
164
+ MaybePromise < UnproxyOrClone < Unpromisify < TReturn > > >
165
+ : unknown ) &
166
+ // Handle construct signature (if present)
167
+ // The return of construct signatures is always proxied (whether marked or not)
168
+ ( T extends { new ( ...args : infer TArguments ) : infer TInstance }
169
+ ? {
170
+ new (
171
+ ...args : {
172
+ [ I in keyof TArguments ] : ProxyOrClone < TArguments [ I ] > ;
173
+ }
174
+ ) : // The raw constructor could either be sync or async, but is always proxied automatically
175
+ MaybePromise < Local < Unpromisify < TInstance > > > ;
176
+ }
177
+ : unknown ) ;
77
178
78
179
export interface TransferHandler {
79
180
canHandle ( obj : any ) : boolean ;
@@ -317,7 +418,7 @@ export function transfer(obj: any, transfers: Transferable[]) {
317
418
return obj ;
318
419
}
319
420
320
- export function proxy < T > ( obj : T ) : T & { [ proxyMarker ] : true } {
421
+ export function proxy < T > ( obj : T ) : T & ProxyMarked {
321
422
return Object . assign ( obj , { [ proxyMarker ] : true } ) as any ;
322
423
}
323
424
0 commit comments