#Get the union type in a schema as a list
9 messages · Page 1 of 1 (latest)
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:
- You define the validator once and reuse it
- You get type safety with the
Languagetype - 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:
- First, define your validator separately so you can reuse it
- Extract the TypeScript type using
Infer - 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.
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
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.