Repository: nuxt/nuxt
File: packages/nuxt/src/app/composables/asyncData.ts (TypeScript)

Code before:
 * See {@link https://nuxt.com/docs/api/composables/use-lazy-async-data}
 * @param key A unique key to ensure that data fetching can be properly de-duplicated across requests.
 * @param handler An asynchronous function that must return a value (it should not be `undefined`) or the request may be duplicated on the client side.
 * @param options customize the behavior of useLazyAsyncData
 */
export function useLazyAsyncData<
  ResT,
  NuxtErrorDataT = unknown,
  DataT = ResT,
  PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
  DefaultT = DataT,
> (
  key: MaybeRefOrGetter<string>,
  handler: (ctx?: NuxtApp) => Promise<ResT>,
  options?: Omit<AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>, 'lazy'>
): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | undefined>

export function useLazyAsyncData<
  ResT,
  NuxtErrorDataT = unknown,
  DataT = ResT,
  PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
  DefaultT = undefined,
> (...args: any[]): AsyncData<PickFrom<DataT, PickKeys>, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | undefined> {
  const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined
  if (typeof args[0] !== 'string' && typeof args[0] !== 'object' && !(typeof args[0] === 'function' && typeof args[1] === 'function')) { args.unshift(autoKey) }
  const [key, handler, options = {}] = args as [string, (ctx?: NuxtApp) => Promise<ResT>, AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>]

  if (import.meta.dev) {
    // @ts-expect-error private property
    options._functionName ||= 'useLazyAsyncData'
  }

  // @ts-expect-error we pass an extra argument to prevent a key being injected
  return useAsyncData(key, handler, { ...options, lazy: true }, null)
}

/** @since 3.1.0 */
export function useNuxtData<DataT = any> (key: string): { data: Ref<DataT | undefined> } {
  const nuxtApp = useNuxtApp()

  // Initialize value when key is not already set
  if (!(key in nuxtApp.payload.data)) {
    nuxtApp.payload.data[key] = asyncDataDefaults.value
  }

  if (nuxtApp._asyncData[key]) {
    const data = nuxtApp._asyncData[key]
    data._deps++
    if (getCurrentScope()) {
      onScopeDispose(() => {

Review comment (suggestion):
The condition `typeof args[0] !== 'object'` will treat `null` (where `typeof null === 'object'`) as a valid key, which may lead to unintended behavior. Consider explicitly checking for `args[0] != null` before accepting object keys.
```suggestion
  if (args[0] != null && typeof args[0] !== 'string' && typeof args[0] !== 'object' && !(typeof args[0] === 'function' && typeof args[1] === 'function')) { args.unshift(autoKey) }
```

Code after:
 * See {@link https://nuxt.com/docs/api/composables/use-lazy-async-data}
 * @param key A unique key to ensure that data fetching can be properly de-duplicated across requests.
 * @param handler An asynchronous function that must return a value (it should not be `undefined`) or the request may be duplicated on the client side.
 * @param options customize the behavior of useLazyAsyncData
 */
export function useLazyAsyncData<
  ResT,
  NuxtErrorDataT = unknown,
  DataT = ResT,
  PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
  DefaultT = DataT,
> (
  key: MaybeRefOrGetter<string>,
  handler: (ctx?: NuxtApp) => Promise<ResT>,
  options?: Omit<AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>, 'lazy'>
): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | undefined>

export function useLazyAsyncData<
  ResT,
  NuxtErrorDataT = unknown,
  DataT = ResT,
  PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
  DefaultT = undefined,
> (...args: any[]): AsyncData<PickFrom<DataT, PickKeys>, (NuxtErrorDataT extends Error | NuxtError ? NuxtErrorDataT : NuxtError<NuxtErrorDataT>) | undefined> {
  const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined
  if (_isAutoKeyNeeded(args[0], args[1])) { args.unshift(autoKey) }
  const [key, handler, options = {}] = args as [string, (ctx?: NuxtApp) => Promise<ResT>, AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>]

  if (import.meta.dev) {
    // @ts-expect-error private property
    options._functionName ||= 'useLazyAsyncData'
  }

  // @ts-expect-error we pass an extra argument to prevent a key being injected
  return useAsyncData(key, handler, { ...options, lazy: true }, null)
}

function _isAutoKeyNeeded (keyOrFetcher: string | MaybeRefOrGetter<string> | (() => any), fetcher: () => any): boolean {
  // string key
  if (typeof keyOrFetcher === 'string') {
    return false
  }
  // ref or computed key
  if (typeof keyOrFetcher === 'object' && keyOrFetcher !== null) {
    return false
  }
  // getter key only if it's followed by a getter function
  if (typeof keyOrFetcher === 'function' && typeof fetcher === 'function') {
    return false
  }
  return true