我有一个在整个应用程序中使用的 FormMeta
类型:
export type FormMeta = {
validationSchema?: EntityValidationSchemaType;
fields: { [fieldName: string]: FormField };
inEditMode?: boolean;
isDirty?: boolean;
generalErrors?: { error: string }[];
};
此 fields
的 FormMeta
属性非常模糊,并禁用了许多我希望以某种方式修复的 IntelliSense 功能。
无论我在何处创建此 FormMeta
类型的对象,我都可以正确填写 fields
:
export const formMetaFilter = (): FormMeta => ({
fields: {
filter: { ... },
name: { ... },
sortBy: { ... },
},
});
如果我不将此对象输入为 FormMeta
,TS 将能够正确建议所有(且仅)fields
非常棒且正是我想要的:
然而,其他需要 FormMeta
类型的方法不会接受这个无类型对象,因为 'filter' !== [key: string]
:
当我将对象键入为 FormMeta
类型时,这些方法将接受该对象,但 IntelliSense 会在 [key: string]
部分发生故障,仅在下一个 .
之后提供自动完成,其中转向导致容易出错的代码。
我想要达到的目标:
我目前使用以下代码:
export type FormMeta<T extends string> = {
fields: { [fieldName in T]: FormField };
// ...
};
export const formMetaFilter = (): FormMeta<'filter' | 'name' | 'sortBy'> => ({
fields: {
filter: { ... },
name: { ... },
sortBy: { ... },
},
});
hasEntityErrors(formMeta: FormMeta<string>): boolean { ... }
实现了我想要的一切:正确的自动完成、检测不存在的 fieldNames 和正确的 FormMeta
识别,但编写和维护非常繁琐。有没有办法让 Typescript 查看这些更详细的键并在没有手动提示的情况下否决 [key: string]
?我的 FormMeta
类型可以轻松容纳多达 50 个 fields
并且我在整个代码库中使用了数百个...
我想在不使用类的情况下解决这个问题。允许使用接口,但仍首选类型。
答案 0 :(得分:1)
我想我通过实现一个 createFormMeta
函数来实现您正在寻找的东西,该函数将字段与元数据的其余部分分开。
这避免了您必须像在 FormMeta<"filter" | "name" | "sortBy">
中那样手动编写字段类型。
我不清楚这个解决方案是否适合您的特定用例。
type EntityValidationSchemaType = any; // not provided in the question
type FormField = any; // FormField was not provided in the question
type FormMetaWithoutFields = {
validationSchema?: EntityValidationSchemaType;
inEditMode?: boolean;
isDirty?: boolean;
generalErrors?: { error: string }[];
}
type FormMeta<T extends Record<string, FormField>> = FormMetaWithoutFields & {
fields: T;
};
const createFormMeta = <T extends Record<string, FormField>>(fields: T, meta: FormMetaWithoutFields): FormMeta<T> => {
return { ...meta, fields };
};
// You can hover on this function and see that it automatically infers
// the return type as
// FormMeta<{
// fields: {
// filter: string;
// name: string;
// sortBy: string;
// };
// }>
const formMetaFilter = () => createFormMeta(
{
fields: {
filter: "",
name: "",
sortBy: "",
},
},
{} // empty metadata, but this can contain properties like `validationSchema` etc.
);
// To be implemented
declare const hasEntityErrors: <T extends Record<string, FormField>>(formMeta: FormMeta<T>) => boolean