Skip to content

Commit cc0e3d7

Browse files
authored
fix(atoms): make InjectPromiseAtomApi methods return the subtype (#320)
1 parent 353aa74 commit cc0e3d7

File tree

4 files changed

+71
-36
lines changed

4 files changed

+71
-36
lines changed

packages/atoms/src/classes/AtomApi.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,9 @@ export class AtomApi<G extends AtomApiGenerics> {
125125
return this as unknown as AtomApi<Omit<G, 'Promise'> & { Promise: P }> // for chaining
126126
}
127127

128-
public setTtl(ttl: AtomInstanceTtl | (() => AtomInstanceTtl)) {
128+
public setTtl(ttl: AtomInstanceTtl | (() => AtomInstanceTtl)): AtomApi<G> {
129129
this.ttl = ttl
130130

131-
return this // for chaining
131+
return this as AtomApi<G> // for chaining
132132
}
133133
}

packages/atoms/src/factories/ion.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -107,34 +107,6 @@ export const ion: {
107107
ResolvedState: ResolvedState
108108
}>
109109

110-
// No Signal (TODO: Is this overload unnecessary? `atom` doesn't have it)
111-
<
112-
State = any,
113-
Params extends any[] = [],
114-
Exports extends Record<string, any> = None,
115-
PromiseType extends AtomApiPromise = undefined
116-
>(
117-
key: string,
118-
value: (
119-
ecosystem: Ecosystem,
120-
...params: Params
121-
) =>
122-
| AtomApi<{
123-
Exports: Exports
124-
Promise: PromiseType
125-
Signal: undefined
126-
State: State
127-
}>
128-
| State,
129-
config?: AtomConfig<State>
130-
): IonTemplateRecursive<{
131-
State: State
132-
Params: Params
133-
Events: None
134-
Exports: Exports
135-
Promise: PromiseType
136-
}>
137-
138110
// Catch-all
139111
<
140112
State = any,

packages/atoms/src/injectors/injectPromise.ts

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ import {
66
} from '../utils/promiseUtils'
77
import {
88
AtomApiGenerics,
9+
AtomInstanceTtl,
10+
ExportsConfig,
911
InjectorDeps,
1012
InjectPromiseConfig,
1113
InjectSignalConfig,
1214
InternalEvaluationReason,
1315
None,
16+
Prettify,
1417
PromiseState,
1518
} from '../types/index'
1619
import { injectEffect } from './injectEffect'
@@ -34,6 +37,43 @@ export interface InjectPromiseAtomApi<
3437
ResolvedState: Data
3538
State: Data | undefined
3639
}>
40+
41+
addExports<NewExports extends Record<string, any>>(
42+
exports: NewExports,
43+
config?: ExportsConfig
44+
): InjectPromiseAtomApi<
45+
Prettify<
46+
Omit<G, 'Exports'> & {
47+
Exports: (G['Exports'] extends Record<string, never>
48+
? unknown
49+
: G['Exports']) &
50+
NewExports
51+
}
52+
>,
53+
EventMap,
54+
Data
55+
>
56+
57+
setExports<NewExports extends Record<string, any>>(
58+
exports: NewExports,
59+
config?: ExportsConfig
60+
): InjectPromiseAtomApi<
61+
Prettify<Omit<G, 'Exports'> & { Exports: NewExports }>,
62+
EventMap,
63+
Data
64+
>
65+
66+
setPromise<P extends Promise<any> | undefined>(
67+
promise?: P
68+
): InjectPromiseAtomApi<
69+
Prettify<Omit<G, 'Promise'> & { Promise: P }>,
70+
EventMap,
71+
Data
72+
>
73+
74+
setTtl(
75+
ttl: AtomInstanceTtl | (() => AtomInstanceTtl)
76+
): InjectPromiseAtomApi<G, EventMap, Data>
3777
}
3878

3979
const hasInvalidateReason = (node: ReturnType<typeof injectSelf>) => {
@@ -111,7 +151,7 @@ export const injectPromise: {
111151
} & InjectSignalConfig<EventMap>
112152
): InjectPromiseAtomApi<
113153
{
114-
Exports: Record<string, any>
154+
Exports: None
115155
Promise: Promise<Data>
116156
Signal: MappedSignal<{
117157
Events: EventMap
@@ -132,7 +172,7 @@ export const injectPromise: {
132172
config?: InjectPromiseConfig<Data> & InjectSignalConfig<EventMap>
133173
): InjectPromiseAtomApi<
134174
{
135-
Exports: Record<string, any>
175+
Exports: None
136176
Promise: Promise<Data>
137177
Signal: MappedSignal<{
138178
Events: EventMap
@@ -246,7 +286,7 @@ export const injectPromise: {
246286
refs.current.promise
247287
) as unknown as InjectPromiseAtomApi<
248288
{
249-
Exports: Record<string, any>
289+
Exports: None
250290
Promise: Promise<Data>
251291
Signal: MappedSignal<{
252292
Events: EventMap

packages/react/test/types.test.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,11 +1031,33 @@ describe('react types', () => {
10311031
})
10321032

10331033
test('ResolvedState', async () => {
1034-
const atom1 = atom('1', () => injectPromise(() => Promise.resolve(1), []))
1035-
const ion1 = ion('1', () => injectPromise(() => Promise.resolve(1), []))
1034+
const atom1 = atom('1', () => {
1035+
const promiseApi = injectPromise(() => Promise.resolve(1), [])
1036+
const withExports = promiseApi.setExports({ a: 'a' })
1037+
const withTtl = withExports.setTtl(1)
1038+
1039+
// InjectPromiseAtomApi method return values retain the subtype
1040+
expectTypeOf(withTtl.dataSignal.get()).toEqualTypeOf<number | undefined>()
1041+
1042+
return withTtl
1043+
})
1044+
1045+
const ion1 = ion('1', () => {
1046+
const promiseApi = injectPromise(() => Promise.resolve(1), [])
1047+
const withExports = promiseApi.addExports({ b: 'b' })
1048+
const withStringPromise = withExports.setPromise(Promise.resolve('b'))
1049+
1050+
// InjectPromiseAtomApi method return values retain the subtype
1051+
expectTypeOf(withStringPromise.dataSignal.get()).toEqualTypeOf<
1052+
number | undefined
1053+
>()
1054+
1055+
return withStringPromise
1056+
})
10361057

10371058
expectTypeOf<StateOf<typeof atom1>>().toEqualTypeOf<PromiseState<number>>()
10381059
expectTypeOf(ecosystem.get(atom1).data).toEqualTypeOf<number | undefined>()
1060+
expectTypeOf<ExportsOf<typeof atom1>>().toEqualTypeOf<{ a: string }>()
10391061
expectTypeOf(await ecosystem.getNode(atom1).promise).toEqualTypeOf<number>()
10401062
expectTypeOf<ResolvedStateOf<typeof atom1>>().toEqualTypeOf<
10411063
Omit<PromiseState<number>, 'data'> & {
@@ -1045,7 +1067,8 @@ describe('react types', () => {
10451067

10461068
expectTypeOf<StateOf<typeof ion1>>().toEqualTypeOf<PromiseState<number>>()
10471069
expectTypeOf(ecosystem.get(ion1).data).toEqualTypeOf<number | undefined>()
1048-
expectTypeOf(await ecosystem.getNode(ion1).promise).toEqualTypeOf<number>()
1070+
expectTypeOf(await ecosystem.getNode(ion1).promise).toEqualTypeOf<string>()
1071+
expectTypeOf<ExportsOf<typeof ion1>>().toEqualTypeOf<{ b: string }>()
10491072
expectTypeOf<ResolvedStateOf<typeof ion1>>().toEqualTypeOf<
10501073
Omit<PromiseState<number>, 'data'> & {
10511074
data: number

0 commit comments

Comments
 (0)