Currently, I am in the process of constructing a form schema that prioritizes type safety. One particular aspect of the form requires performing keyof checking on a subset of the form type. However, I am facing challenges when it comes to narrowing down and passing the generic type to the sub type.
If you're interested, here is a link to the TypeScript playground where I've attempted to create a simplified version of my ongoing work: playground
My main objective is to ensure that the properties within the fields
section of the FieldArray
are also type safe, just like the Schema type. Unfortunately, I'm struggling with how to effectively narrow down or pass the type for the fields
property.
type FieldType = "text-input" | "number" | "dropdown" | "checkbox";
type Field = {
label: string;
type: FieldType;
};
type FieldName<T> = T[keyof T] extends (infer I)[] ? I : never;
type FieldArray<T> = {
type: "array";
groupLabel: string;
fields: Record<keyof FieldName<T> & string, Field>;
};
type SchemaField<T> = Field | FieldArray<T>;
type Schema<T> = Record<keyof T, SchemaField<T>>;
type Form = {
workflowName: string;
id: number;
rules: { ruleName: string; isActive: boolean; ruleId: number }[];
errors: { errorName: string; isActive: boolean; errorId: number }[];
};
const formSchema: Schema<Form> = {
workflowName: { type: "text-input", label: "Name" },
id: { type: "number", label: "Id" },
rules: {
type: "array",
groupLabel: "Rules",
fields: {
ruleName: { label: "Rule Name", type: "text-input" },
isActive: { label: "Is Active", type: "checkbox" },
ruleId: { label: "Rule Id", type: "number" },
},
},
errors: {
type: "array",
groupLabel: "Errors",
fields: {
errorName: { label: "Error Name", type: "text-input" },
isActive: { label: "Is Active", type: "checkbox" },
errorId: { label: "Error Id", type: "number" },
},
},
};
My expectation is that the fields property within rules
and errors
should adhere strictly to their definitions in Form
.
For instance, any attempt to add additional properties in the fields object of errors
beyond errorName
, isActive
, and errorId
as defined in the Schema should trigger a type warning. However, this is not happening currently.
errors: {
fields: {
foo: { label: "Error Name", type: "text-input" },
bar: { label: "Is Active", type: "checkbox" },
baz: { label: "Error Id", type: "number" },
},
},