Skip to content

Commit cc2f421

Browse files
committed
Fix types
1 parent 0082ce7 commit cc2f421

File tree

1 file changed

+145
-44
lines changed

1 file changed

+145
-44
lines changed

src/comlink.ts

Lines changed: 145 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -27,53 +27,154 @@ export const createEndpoint = Symbol("Comlink.endpoint");
2727
export const releaseProxy = Symbol("Comlink.releaseProxy");
2828
const throwSet = new WeakSet();
2929

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;
3788

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+
*/
39122
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;
73144

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;
75149

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);
77178

78179
export interface TransferHandler {
79180
canHandle(obj: any): boolean;
@@ -317,7 +418,7 @@ export function transfer(obj: any, transfers: Transferable[]) {
317418
return obj;
318419
}
319420

320-
export function proxy<T>(obj: T): T & { [proxyMarker]: true } {
421+
export function proxy<T>(obj: T): T & ProxyMarked {
321422
return Object.assign(obj, { [proxyMarker]: true }) as any;
322423
}
323424

0 commit comments

Comments
 (0)