schemix
schemix copied to clipboard
Additional type safety for enum default values
Given an enum
const PlanType = createEnum((PlanType) => {
PlanType
.addValue("FREE")
.addValue("PREMIUM")
})
and a model
const Plan = createModel((Plan) => {
Plan
.string("name", { id: true })
.enum("type", PlanType, { default: "FREE" })
})
I want to ensure that the value passed as the default argument for the enum (e.g. { default: "FREE" }
) is a valid value of the enum (i.e. ["FREE", "PREMIUM"]
).
At the moment I am able to pass anything (e.g. .enum("type", PlanType, { default: "SOMETHING" })
).
FWIW I had a little play with this and it ends up looking like
export class PrismaEnum<Values = never> {
private enumMap: Map<string, string | undefined> = new Map();
constructor(public readonly name: string) {}
public addValue<Values, Value extends string>(
this: PrismaEnum<Values>,
value: Value,
options?: PrismaEnumOptions
): PrismaEnum<Values | Value> {
// We need to return a new instance with a cloned `enumMap`
// for the types to do what we want.
// If we modify `enumMap` in place then the type of `this` will be "wrong".
const clone = new PrismaEnum(this.name);
clone.enumMap = new Map(this.enumMap);
clone.enumMap.set(value, options?.map);
return clone;
}
// ...etc
}
Which then gives you typing like
// e.g.
const test: PrismaEnum<"foo" | "baz"> = new PrismaEnum("test")
.addValue("foo")
.addValue("baz");
Although this doesn't fit nicely with the create*
functions where the callback is expected to mutate the values in place...
Are we looking for Intellisense/Type-checking, or runtime checks?
Since, as @jmackie specified, the values are altered in place we'd have to go a roundabout way of making the values available to the TypeScript language server. One way would be to return the value of the alteration and build the strings onto the enum constructor type. Otherwise, we could have it default to "string" as we currently do.
Another way would be supporting as such.
const PlanType = createEnum<"foo", "baz">((PlanType) => {
PlanType
.addValue("foo")
.addValue("baz")
})
A little verbose, though.