Skip to content

Commit 10112a0

Browse files
authored
fix(loaders): make data always possibly undefined (#506)
BREAKING CHANGE: `data` will always allow `undefined` for the initial value and possible error states. The current type is no longer accurate. In practice, this shouldn't require many changes in the user code base
1 parent 5c8f397 commit 10112a0

File tree

8 files changed

+72
-119
lines changed

8 files changed

+72
-119
lines changed

src/data-loaders/createDataLoader.ts

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,11 @@ import { type _Awaitable } from '../utils'
88
/**
99
* Base type for a data loader entry. Each Data Loader has its own entry in the `loaderEntries` (accessible via `[LOADER_ENTRIES_KEY]`) map.
1010
*/
11-
export interface DataLoaderEntryBase<
12-
isLazy extends boolean = boolean,
13-
Data = unknown,
14-
> {
11+
export interface DataLoaderEntryBase<Data = unknown> {
1512
/**
1613
* Data stored in the entry.
1714
*/
18-
data: ShallowRef<_DataMaybeLazy<Data, isLazy>>
15+
data: ShallowRef<Data | undefined>
1916

2017
/**
2118
* Error if there was an error.
@@ -32,7 +29,7 @@ export interface DataLoaderEntryBase<
3229
*/
3330
isLoading: ShallowRef<boolean>
3431

35-
options: DefineDataLoaderOptionsBase<isLazy>
32+
options: DefineDataLoaderOptionsBase
3633

3734
/**
3835
* Called by the navigation guard when the navigation is duplicated. Should be used to reset pendingTo and pendingLoad and any other property that should be reset.
@@ -86,15 +83,15 @@ export interface CreateDataLoaderOptions<
8683
after: <Data = unknown>(data: Data, context: Context) => unknown
8784
}
8885

89-
export interface DefineDataLoaderOptionsBase<isLazy extends boolean> {
86+
export interface DefineDataLoaderOptionsBase {
9087
/**
9188
* Whether the data should be lazy loaded without blocking the client side navigation or not. When set to true, the loader will no longer block the navigation and the returned composable can be called even
9289
* without having the data ready.
9390
*
9491
* @defaultValue `false`
9592
*/
9693
lazy?:
97-
| isLazy
94+
| boolean
9895
| ((
9996
to: RouteLocationNormalizedLoaded,
10097
from?: RouteLocationNormalizedLoaded
@@ -143,7 +140,6 @@ export const toLazyValue = (
143140
* - `after-load`: the data is committed after all non-lazy loaders have finished loading.
144141
*/
145142
export type DefineDataLoaderCommit = 'immediate' | 'after-load'
146-
// TODO: is after-load fine or is it better to have an after-navigation instead
147143

148144
export interface DataLoaderContextBase {
149145
/**
@@ -153,11 +149,11 @@ export interface DataLoaderContextBase {
153149
}
154150

155151
export interface DefineDataLoader<Context extends DataLoaderContextBase> {
156-
<isLazy extends boolean, Data>(
152+
<Data>(
157153
fn: DefineLoaderFn<Data, Context>,
158-
options?: DefineDataLoaderOptionsBase<isLazy>
154+
options?: DefineDataLoaderOptionsBase
159155
// TODO: or a generic that allows a more complex UseDataLoader
160-
): UseDataLoader<isLazy, Data>
156+
): UseDataLoader<Data>
161157
}
162158

163159
// TODO: should be in each data loader. Refactor the base type to accept the needed generics
@@ -166,10 +162,7 @@ export interface DefineDataLoader<Context extends DataLoaderContextBase> {
166162
* Data Loader composable returned by `defineLoader()`.
167163
* @see {@link DefineDataLoader}
168164
*/
169-
export interface UseDataLoader<
170-
isLazy extends boolean = boolean,
171-
Data = unknown,
172-
> {
165+
export interface UseDataLoader<Data = unknown> {
173166
[IS_USE_DATA_LOADER_KEY]: true
174167

175168
/**
@@ -198,24 +191,21 @@ export interface UseDataLoader<
198191
// `return new NavigationResult()` in the loader
199192
Exclude<Data, NavigationResult>,
200193
// or use it as a composable
201-
UseDataLoaderResult<isLazy, Exclude<Data, NavigationResult>>
194+
UseDataLoaderResult<Exclude<Data, NavigationResult>>
202195
>
203196

204197
/**
205198
* Internals of the data loader.
206199
* @internal
207200
*/
208-
_: UseDataLoaderInternals<isLazy, Exclude<Data, NavigationResult>>
201+
_: UseDataLoaderInternals<Exclude<Data, NavigationResult>>
209202
}
210203

211204
/**
212205
* Internal properties of a data loader composable. Used by the internal implementation of `defineLoader()`. **Should
213206
* not be used in application code.**
214207
*/
215-
export interface UseDataLoaderInternals<
216-
isLazy extends boolean = boolean,
217-
Data = unknown,
218-
> {
208+
export interface UseDataLoaderInternals<Data = unknown> {
219209
/**
220210
* Loads the data from the cache if possible, otherwise loads it from the loader and awaits it.
221211
*
@@ -234,36 +224,25 @@ export interface UseDataLoaderInternals<
234224
/**
235225
* Resolved options for the loader.
236226
*/
237-
options: DefineDataLoaderOptionsBase<isLazy>
227+
options: DefineDataLoaderOptionsBase
238228

239229
/**
240230
* Gets the entry associated with the router instance. Assumes the data loader has been loaded and that the entry
241231
* exists.
242232
*
243233
* @param router - router instance
244234
*/
245-
getEntry(router: Router): DataLoaderEntryBase<isLazy, Data>
235+
getEntry(router: Router): DataLoaderEntryBase<Data>
246236
}
247237

248-
/**
249-
* Generates the type for a `Ref` of a data loader based on the value of `lazy`.
250-
* @internal
251-
*/
252-
export type _DataMaybeLazy<Data, isLazy extends boolean = boolean> =
253-
// no lazy provided, default value is false
254-
boolean extends isLazy ? Data : true extends isLazy ? Data | undefined : Data
255-
256238
/**
257239
* Return value of a loader composable defined with `defineLoader()`.
258240
*/
259-
export interface UseDataLoaderResult<
260-
isLazy extends boolean = boolean,
261-
Data = unknown,
262-
> {
241+
export interface UseDataLoaderResult<Data = unknown> {
263242
/**
264243
* Data returned by the loader. If the data loader is lazy, it will be undefined until the first load.
265244
*/
266-
data: ShallowRef<_DataMaybeLazy<Data, isLazy>>
245+
data: ShallowRef<Data | undefined>
267246

268247
/**
269248
* Whether there is an ongoing request.

src/data-loaders/defineColadaLoader.ts

Lines changed: 30 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import type {
1313
DefineLoaderFn,
1414
UseDataLoader,
1515
UseDataLoaderResult,
16-
_DataMaybeLazy,
1716
_DefineLoaderEntryMap,
1817
} from 'unplugin-vue-router/runtime'
1918
import {
@@ -60,40 +59,32 @@ import { toLazyValue } from './createDataLoader'
6059
* @param loader - function that returns a promise with the data
6160
* @param options - options to configure the data loader
6261
*/
63-
export function defineColadaLoader<
64-
Name extends keyof RouteMap,
65-
Data,
66-
isLazy extends boolean,
67-
>(
62+
export function defineColadaLoader<Name extends keyof RouteMap, Data>(
6863
name: Name,
69-
options: DefineDataColadaLoaderOptions<isLazy, Name, Data>
70-
): UseDataLoaderColada<isLazy, Data>
71-
export function defineColadaLoader<Data, isLazy extends boolean>(
72-
options: DefineDataColadaLoaderOptions<isLazy, keyof RouteMap, Data>
73-
): UseDataLoaderColada<isLazy, Data>
64+
options: DefineDataColadaLoaderOptions<Name, Data>
65+
): UseDataLoaderColada<Data>
66+
export function defineColadaLoader<Data>(
67+
options: DefineDataColadaLoaderOptions<keyof RouteMap, Data>
68+
): UseDataLoaderColada<Data>
7469

75-
export function defineColadaLoader<Data, isLazy extends boolean>(
70+
export function defineColadaLoader<Data>(
7671
nameOrOptions:
7772
| keyof RouteMap
78-
| DefineDataColadaLoaderOptions<isLazy, keyof RouteMap, Data>,
79-
_options?: DefineDataColadaLoaderOptions<isLazy, keyof RouteMap, Data>
80-
): UseDataLoaderColada<isLazy, Data> {
73+
| DefineDataColadaLoaderOptions<keyof RouteMap, Data>,
74+
_options?: DefineDataColadaLoaderOptions<keyof RouteMap, Data>
75+
): UseDataLoaderColada<Data> {
8176
// TODO: make it DEV only and remove the first argument in production mode
8277
// resolve option overrides
8378
_options =
8479
_options ||
85-
(nameOrOptions as DefineDataColadaLoaderOptions<
86-
isLazy,
87-
keyof RouteMap,
88-
Data
89-
>)
80+
(nameOrOptions as DefineDataColadaLoaderOptions<keyof RouteMap, Data>)
9081
const loader = _options.query
9182

9283
const options = {
9384
...DEFAULT_DEFINE_LOADER_OPTIONS,
9485
..._options,
9586
commit: _options?.commit || 'after-load',
96-
} as DefineDataColadaLoaderOptions<isLazy, keyof RouteMap, Data>
87+
} as DefineDataColadaLoaderOptions<keyof RouteMap, Data>
9788

9889
let isInitial = true
9990

@@ -104,18 +95,16 @@ export function defineColadaLoader<Data, isLazy extends boolean>(
10495
parent?: DataLoaderEntryBase,
10596
reload?: boolean
10697
): Promise<void> {
107-
const entries = router[
108-
LOADER_ENTRIES_KEY
109-
]! as unknown as _DefineLoaderEntryMap<
110-
DataLoaderColadaEntry<boolean, unknown>
98+
const entries = router[LOADER_ENTRIES_KEY]! as _DefineLoaderEntryMap<
99+
DataLoaderColadaEntry<unknown>
111100
>
112101
const isSSR = router[IS_SSR_KEY]
113102
const key = serializeQueryKey(options.key, to)
114103
if (!entries.has(loader)) {
115104
const route = shallowRef<RouteLocationNormalizedLoaded>(to)
116105
entries.set(loader, {
117106
// force the type to match
118-
data: shallowRef<_DataMaybeLazy<Data, isLazy>>(),
107+
data: shallowRef<Data | undefined>(),
119108
isLoading: shallowRef(false),
120109
error: shallowRef<any>(),
121110
to,
@@ -308,7 +297,7 @@ export function defineColadaLoader<Data, isLazy extends boolean>(
308297
}
309298

310299
function commit(
311-
this: DataLoaderColadaEntry<isLazy, Data>,
300+
this: DataLoaderColadaEntry<Data>,
312301
to: RouteLocationNormalizedLoaded
313302
) {
314303
const key = serializeQueryKey(options.key, to)
@@ -358,7 +347,7 @@ export function defineColadaLoader<Data, isLazy extends boolean>(
358347

359348
// @ts-expect-error: requires the internals and symbol that are added later
360349
const useDataLoader: // for ts
361-
UseDataLoaderColada<isLazy, Data> = () => {
350+
UseDataLoaderColada<Data> = () => {
362351
// work with nested data loaders
363352
const currentEntry = getCurrentContext()
364353
const [parentEntry, _router, _route] = currentEntry
@@ -368,10 +357,8 @@ export function defineColadaLoader<Data, isLazy extends boolean>(
368357

369358
const entries = router[
370359
LOADER_ENTRIES_KEY
371-
]! as unknown as _DefineLoaderEntryMap<
372-
DataLoaderColadaEntry<boolean, unknown>
373-
>
374-
let entry = entries.get(loader)
360+
]! as unknown as _DefineLoaderEntryMap<DataLoaderColadaEntry<unknown>>
361+
let entry = entries.get(loader) as DataLoaderColadaEntry<Data> | undefined
375362

376363
if (
377364
// if the entry doesn't exist, create it with load and ensure it's loading
@@ -388,7 +375,7 @@ export function defineColadaLoader<Data, isLazy extends boolean>(
388375
)
389376
}
390377

391-
entry = entries.get(loader)!
378+
entry = entries.get(loader)! as DataLoaderColadaEntry<Data>
392379

393380
// add ourselves to the parent entry children
394381
if (parentEntry) {
@@ -450,7 +437,7 @@ export function defineColadaLoader<Data, isLazy extends boolean>(
450437
asyncStatus: ext!.asyncStatus,
451438
state: ext!.state,
452439
isPending: ext!.isPending,
453-
} satisfies UseDataLoaderColadaResult<boolean, unknown>
440+
} satisfies UseDataLoaderColadaResult<Data>
454441

455442
// load ensures there is a pending load
456443
const promise = entry
@@ -489,11 +476,10 @@ export function defineColadaLoader<Data, isLazy extends boolean>(
489476
export const joinKeys = (keys: string[]): string => keys.join('|')
490477

491478
export interface DefineDataColadaLoaderOptions<
492-
isLazy extends boolean,
493479
Name extends keyof RouteMap,
494480
Data,
495-
> extends DefineDataLoaderOptionsBase<isLazy>,
496-
Omit<UseQueryOptions<unknown>, 'query' | 'key'> {
481+
> extends DefineDataLoaderOptionsBase,
482+
Omit<UseQueryOptions<Data>, 'query' | 'key'> {
497483
/**
498484
* Key associated with the data and passed to pinia colada
499485
* @param to - Route to load the data
@@ -517,8 +503,8 @@ export interface DefineDataColadaLoaderOptions<
517503
*/
518504
export interface DataColadaLoaderContext extends DataLoaderContextBase {}
519505

520-
export interface UseDataLoaderColadaResult<isLazy extends boolean, Data>
521-
extends UseDataLoaderResult<isLazy, Data>,
506+
export interface UseDataLoaderColadaResult<Data>
507+
extends UseDataLoaderResult<Data>,
522508
Pick<
523509
UseQueryReturn<Data, any>,
524510
'isPending' | 'refetch' | 'refresh' | 'status' | 'asyncStatus' | 'state'
@@ -527,8 +513,7 @@ export interface UseDataLoaderColadaResult<isLazy extends boolean, Data>
527513
/**
528514
* Data Loader composable returned by `defineColadaLoader()`.
529515
*/
530-
export interface UseDataLoaderColada<isLazy extends boolean, Data>
531-
extends UseDataLoader<isLazy, Data> {
516+
export interface UseDataLoaderColada<Data> extends UseDataLoader<Data> {
532517
/**
533518
* Data Loader composable returned by `defineColadaLoader()`.
534519
*
@@ -555,12 +540,11 @@ export interface UseDataLoaderColada<isLazy extends boolean, Data>
555540
// `return new NavigationResult()` in the loader
556541
Exclude<Data, NavigationResult>,
557542
// or use it as a composable
558-
UseDataLoaderColadaResult<isLazy, Exclude<Data, NavigationResult>>
543+
UseDataLoaderColadaResult<Exclude<Data, NavigationResult>>
559544
>
560545
}
561546

562-
export interface DataLoaderColadaEntry<isLazy extends boolean, Data>
563-
extends DataLoaderEntryBase<isLazy, Data> {
547+
export interface DataLoaderColadaEntry<Data> extends DataLoaderEntryBase<Data> {
564548
/**
565549
* Reactive route passed to pinia colada so it automatically refetch
566550
*/
@@ -601,7 +585,7 @@ const DEFAULT_DEFINE_LOADER_OPTIONS = {
601585
server: true,
602586
commit: 'after-load',
603587
} satisfies Omit<
604-
DefineDataColadaLoaderOptions<boolean, keyof RouteMap, unknown>,
588+
DefineDataColadaLoaderOptions<keyof RouteMap, unknown>,
605589
'key' | 'query'
606590
>
607591

@@ -622,7 +606,7 @@ const toValueWithParameters = <T, Arg>(
622606
* @param to - route to use
623607
*/
624608
function serializeQueryKey(
625-
keyOption: DefineDataColadaLoaderOptions<boolean, string, unknown>['key'],
609+
keyOption: DefineDataColadaLoaderOptions<string, unknown>['key'],
626610
to: RouteLocationNormalizedLoaded
627611
): string[] {
628612
const key = toValueWithParameters(keyOption, to)

src/data-loaders/defineLoader.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { RouteLocationNormalizedLoaded } from 'vue-router'
3333

3434
function mockedLoader<T = string | NavigationResult>(
3535
// boolean is easier to handle for router mock
36-
options?: DefineDataLoaderOptions<boolean>
36+
options?: DefineDataLoaderOptions
3737
) {
3838
const [spy, resolve, reject] = mockPromise<T, unknown>(
3939
// not correct as T could be something else

0 commit comments

Comments
 (0)