#Why the users are being considered as null (type inferance)

9 messages · Page 1 of 1 (latest)

tall edge
#

import { query } from "./_generated/server";
import { paginationOptsValidator } from "convex/server";
import { getAll } from "convex-helpers/server/relationships";

export const list = query({
args: { paginationOpts: paginationOptsValidator },
handler: async (ctx, { paginationOpts }) => {
// Fetch a page of tutors
const results = await ctx.db.query("tutors").paginate(paginationOpts);

// Extract user IDs from the tutors
const userIds = results.page.map((t) => t.user);

// Fetch all user documents corresponding to the user IDs
const users = await getAll(ctx.db, userIds);

// Combine tutor and user information
const page = await Promise.all(
  results.page.map(async (tutor) => {
    const user = users.find((u) => u._id === tutor.user); // THIS IS NOT NULLABLE...

    // Generate the URL for the user's profile image
    const imageUrl = await ctx.storage.getUrl(user!.image);

    return {
      ...tutor,
      user: {
        ...user,
        image: imageUrl,
      },
    };
  })
);

return {
  ...results,
  page,
};

},
});

old zenithBOT
#

Hi @tall edge. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

Based on the provided code and the question, it seems the issue is related to type inference for the user object within the Promise.all mapping function. Let's analyze why TypeScript might be considering the user as potentially null:

  1. The getAll function from convex-helpers is used to fetch user documents. This function likely returns an array of user objects, but TypeScript might not be able to guarantee that all requested users were found.

  2. The find method used on the users array can potentially return undefined if no matching user is found.

  3. TypeScript is inferring that user could be undefined (which is treated as null in many contexts) because of the possibility that find doesn't locate a matching user.

To address this issue, you have a few options:

  1. Use a non-null assertion operator (!) as you've done with user!.image. This tells TypeScript that you're certain user will not be null or undefined. However, this approach can be risky if there's any chance the user might not exist.

  2. Add a null check before accessing user properties:

const page = await Promise.all(
  results.page.map(async (tutor) => {
    const user = users.find((u) => u._id === tutor.user);
    
    if (!user) {
      // Handle the case where user is not found
      return { ...tutor, user: null };
    }

    const imageUrl = await ctx.storage.getUrl(user.image);

    return {
      ...tutor,
      user: {
        ...user,
        image: imageUrl,
      },
    };
  })
);
  1. Use optional chaining and provide a default value:
const imageUrl = await ctx.storage.getUrl(user?.image ?? 'default-image-id');
#
  1. If you're certain that every tutor has a corresponding user, you could use getAll with the orThrow option (if available) to ensure it throws an error if any user is not found. This would make TypeScript infer that all users exist.

To improve type safety and make your intentions clear to TypeScript, I recommend using option 2 or 3, as they handle the potential absence of a user more gracefully.

For more information on working with Convex and TypeScript, you might want to check out the Convex documentation on TypeScript best practices (https://docs.convex.dev/production/best-practices/typescript).

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@tall edge Kapa.ai is still learning and improving, please let me know how I did by reacting below.

tall edge
#

I want to skip if null, everything must be non nullable like "continue"

#

?

#

@old zenith

old zenithBOT
#

Let me check the knowledge base for you again. One moment! :books: