Reputation: 631
I need to make a property accept a predefined set of string literals, but it can also accept any string values.
This is how I did it in typescript
const PREDEFINED_VALUES = ['value1', 'value2', 'value3'] as const;
type TStaticValues = typeof PREDEFINED_VALUES[number]
type Values = (string & Record<string, unknown>) | TStaticValues;
With this, I can benefit from intellisense of predefined values to check for. I tried to do it in zod
with the following:
const type = z.object({
// some other types
stringWithPredefinedValues: z.union([
z.string().and(z.object({})),
z.enum(PREDEFINED_VALUES)
])
})
But when I try to check for predefined values, it shows a ts error.
const testValue: string = 'any string should be accepted, but predefined values get detected.';
const check = PREDEFINED_VALUES.includes(testValue)
// ts error: Type 'string & {}' is not assignable to type '"value1" | "value2" | "value3"'.
I am uncertain if my zod
object is correct, or if there is some sort of limitation.
Upvotes: 2
Views: 3338
Reputation: 6081
You were pretty close to the solution. The missing part was re-assigning the array's type to be closer to string[]
. Enum arrays are restricted in terms of array operations whenever they invoke generic functions.
type S<T> = T extends string ? T | string & {} : never;
const PREDEFINED_VALUES = ['value1', 'value2', 'value3'] as const;
// This is what you want
const USABLE_ARRAY: readonly S<typeof PREDEFINED_VALUES[number]>[] = PREDEFINED_VALUES;
const testValue: string = 'any string should be accepted, but predefined values get detected.';
const check = USABLE_ARRAY.includes(testValue);
const check2 = USABLE_ARRAY.includes("");
And intellisense works:
As a sidenote, zod
will never give you types which you can't easily write by hand.
Upvotes: 0