#Get the union type in a schema as a list

9 messages · Page 1 of 1 (latest)

fossil havenBOT
#

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

#

To get a list of values from a union type in your Convex schema, you can use the Infer type from Convex to extract the TypeScript type, and then work with those values in your code. Here's how to approach this:

First, I recommend defining your validator separately so you can reuse it and extract its type:

// in convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v, Infer } from "convex/values";

// Define the validator separately
export const languageValidator = v.union(
  v.literal("english"),
  v.literal("spanish"),
  v.literal("german")
);

// Extract the type
export type Language = Infer<typeof languageValidator>;

export default defineSchema({
  yourTable: defineTable({
    // ... other fields
    language: languageValidator,
    // ... other fields
  }),
});

Now you can create an array of all possible values in a few ways:

// Option 1: Create an array manually using the type
const languages: Language[] = ["english", "spanish", "german"];

// Option 2: If you need to ensure you have all values (type-safe)
// You could create a helper function that ensures all cases are handled
function getLanguageOptions(): Language[] {
  return ["english", "spanish", "german"];
}

This approach has several benefits:

  1. You define the validator once and reuse it
  2. You get type safety with the Language type
  3. You can export both the validator and type for use elsewhere in your app

This pattern is recommended in the Convex documentation as a best practice for keeping validators from going stale and for extracting TypeScript types from validators.

#

Note that Convex doesn't provide a built-in way to automatically extract the literal values from a union validator at runtime, so you'll need to manually list them as shown above.

I hope this helps with your language selection implementation!

GitHub Copilot X: I'll help you extract a list of values from a union type in your Convex schema.

When you define a union of literals in Convex using v.union(), you're creating a validator for runtime type checking. To get a list of the possible values, you need to:

  1. First, define your validator separately so you can reuse it
  2. Extract the TypeScript type using Infer
  3. Create an array with all the possible values

Here's how to do it:

// in convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v, Infer } from "convex/values";

// Define the validator separately
export const languageValidator = v.union(
  v.literal("english"),
  v.literal("spanish"),
  v.literal("german")
);

// Extract the type
export type Language = Infer<typeof languageValidator>;

// Create a constant array with all possible values
export const LANGUAGES: Language[] = ["english", "spanish", "german"];

export default defineSchema({
  yourTable: defineTable({
    // ... other fields
    language: languageValidator,
    // ... other fields
  }),
});

Now you can import and use LANGUAGES anywhere in your application:

import { LANGUAGES } from "../convex/schema";

// Use it in a component
function LanguageSelector() {
  return (
    <select>
      {LANGUAGES.map(lang => (
        <option key={lang} value={lang}>{lang}</option>
      ))}
    </select>
  );
}
#

This approach is recommended in the Convex Types Cookbook as it helps keep your validators and types in sync.

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

#

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

wind crypt
#

if i add another entry to my languageValidator, it will not throw and error for me not having added it to the languages @fossil haven

fossil havenBOT
#

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

#

You're right. If you add another entry to your languageValidator union, TypeScript won't automatically flag an error if you don't update your manually created LANGUAGES array. This is because the array is created separately from the validator, and TypeScript doesn't automatically check that all possible values of the union type are included in the array.

There are a couple of approaches to solve this issue:

1. Use a type assertion with a helper function

You can create a helper function that ensures your array includes all possible values of your union type:

// Helper function to ensure array contains all values of a union type
function createEnum<T extends string, U extends T[]>(values: U & ([T] extends [U[number]] ? unknown : "Missing values")): U {
  return values;
}

// Now TypeScript will error if you don't include all possible values
export const LANGUAGES = createEnum<Language, Language[]>([
  "english", 
  "spanish", 
  "german"
  // If you add a new language to the validator but not here, TypeScript will error
]);

2. Use a const object as an enum

Another approach is to define your languages as a const object first, then create your validator from it:

export const LANGUAGES = {
  ENGLISH: "english",
  SPANISH: "spanish",
  GERMAN: "german"
} as const;

// Create an array of values from the object
export const LANGUAGE_VALUES = Object.values(LANGUAGES);

// Create your validator from the object values
export const languageValidator = v.union(
  ...Object.values(LANGUAGES).map(lang => v.literal(lang))
);

// Extract the type
export type Language = Infer<typeof languageValidator>;

With this approach, you only need to update the LANGUAGES object when adding a new language, and everything else will update automatically.

#

These patterns help ensure type safety and consistency across your application when working with union types in Convex, as recommended in the Types and Validators in TypeScript: A Convex Cookbook guide.

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