forked from reduxjs/redux-toolkit
-
Notifications
You must be signed in to change notification settings - Fork 1
/
suspense-utils.ts
66 lines (54 loc) · 1.67 KB
/
suspense-utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
export interface Resource<Data> {
data?: Data | undefined
isLoading?: boolean
}
export interface IdleResource<Data> extends Resource<Data> {
isSkipped: true
}
export interface Suspendable {
getSuspendablePromise(): Promise<unknown> | undefined
}
export type SuspendableResource<Data> = Resource<Data> & Suspendable
export type IdleSuspendableResource<Data> = IdleResource<Data> & Suspendable
export type ResolvedSuspendableResource<T> = T extends SuspendableResource<
infer Data
>
? Omit<T, 'isLoading' | 'data'> & {
data: Exclude<Data, undefined>
isLoading: false
}
: never
export type UseSuspendAllOutput<Sus extends readonly unknown[]> = {
[K in keyof Sus]: Sus[K] extends IdleSuspendableResource<any>
? Sus[K]
: Sus[K] extends SuspendableResource<any>
? ResolvedSuspendableResource<Sus[K]>
: Sus[K] extends Suspendable
? Sus[K]
: never
}
function isPromiseLike(val: unknown): val is PromiseLike<unknown> {
return (
!!val && typeof val === 'object' && typeof (val as any).then === 'function'
)
}
function getSuspendable(suspendable: Suspendable) {
return suspendable.getSuspendablePromise()
}
export function useSuspendAll<
G extends SuspendableResource<any>,
T extends SuspendableResource<any>[]
>(
...suspendables: readonly [G, ...T]
): UseSuspendAllOutput<readonly [G, ...T]> {
if (!suspendables.length) {
throw new TypeError('useSuspendAll: requires one or more arguments')
}
let promises = suspendables
.map(getSuspendable)
.filter(isPromiseLike) as Promise<unknown>[]
if (promises.length) {
throw Promise.all(promises)
}
return suspendables as UseSuspendAllOutput<readonly [G, ...T]>
}