一个界面中的多个泛型



我正在尝试制作某种解析器,解析数据字段,并将其转换为完整形式并显示它。fields属性将定义 JSON 数据数组中的每个字段,客户端将从url属性接收该字段IForm

表单界面的示例如下所示:

export interface IForm {
name: string;
url: string;
fields: IField<any>[] // <-- What is the proper type of this?
}
export interface IField<T> {
name: string;
label: string;
mandatory?: boolean;
default: T;
}
export interface IInput<T> extends IField<T> {
type: 'input'
}
export interface IOptionsUrl {
url: string;
idField: string;
labelField: string;
}
export interface IOptionsList<T> {
id: T;
label: string;
}
export interface IOptions<T> extends IField<IOptionsList<T>> {
type: 'options';
options?: IOptionsUrl | IOptionsList<T>[] | string[];
multiple?: boolean;
}
export interface ICheckbox extends IField<boolean> {
type: 'checkbox'
}

IForm处的fields属性将包含多种类型,例如IField<string>IField<number>等。它们由每种类型中的type属性确定,全部基于 IField。所以我不确定,我是否应该放<any>因为它将在数组中包含多种类型的数据。定义它的正确方法是什么?还是我应该一起跳过泛型并只使用任何泛型?

示例数据如下所示:

let meta: IForm = {
name: 'employee',
url: '/api/employee',
fields: [
{
type: 'input',
name: 'id',
label: 'Employee ID',
default: 0,
mandatory: true
},
{
type: 'input',
name: 'name',
label: 'Employee Name',
default: '',
mandatory: true
},
{
type: 'options',
name: 'gender',
label: 'Male/Female',
default: 'male',
options: ['Male', 'Female']
},
{
type: 'checkbox',
name: 'active',
label: 'Active',
default: true
},
{
type: 'options',
name: 'department',
label: 'Department',
default: 0,
options: {
url: '/api/departments',
idField: 'id',
labelField: 'name'
}
}
]
}

员工界面将是:

export interface IEmployee {
id: number;
name: string;
gender: string;
active: boolean;
department: number;
}

我应该如何定义 IForm 的接口?

谢谢

我建议,至少考虑到上述信息,你做这样的事情:

type PossibleDataTypes = string | number | boolean; // or whatever you want
type PossibleFields =
| IInput<PossibleDataTypes>
| IOptions<PossibleDataTypes>
| ICheckbox;
export interface IForm {
name: string;
url: string;
fields: Array<PossibleFields>;
}

在这里,我们将field缩小为仅包含您期望的字段类型的数组。 如果需要,可以添加到此列表中。

顺便说一下,我做了另一个更改:

// changed this from IField<IOptionsList<T>> to just IOptionsList<T>
export interface IOptions<T> extends IField<T> {
type: "options";
options?:
| IOptionsUrl
| ReadonlyArray<IOptionsList<T>>
| ReadonlyArray<string>;
multiple?: boolean;
}

因为没有它,您的meta变量不匹配。 另外,我认为meta有一个错字,它使用data而不是url。 无论如何,您可以像以前一样将meta定义为IForm,但是将变量扩大到IForm会使它忘记细节(例如您正在使用的特定字段类型)。如果您只想验证meta是否与IForm匹配而不将其扩大到IForm,则可以使用如下所示的帮助程序函数:

const asIForm = <F extends IForm>(f: F) => f;

然后像这样使用它

const meta = asIForm({
name: "employee",
url: "/api/employee",
fields: [
{
type: "input",
name: "id",
label: "Employee ID",
default: 0,
mandatory: true
},
{
type: "options",
name: "gender",
label: "Male/Female",
default: "male",
options: ["Male", "Female"]
},
{
type: "checkbox",
name: "active",
label: "Active",
default: true
},
{
type: "options",
name: "department",
label: "Department",
default: 0,
options: {
url: "/api/departments",
idField: "id",
labelField: "name"
}
}
]
});

现在,鉴于PossibleFields是一个具体的区分联合,你可以让编译器通过类型保护缩小每个field条目的范围,如下所示:

function processForm(form: IForm) {
for (let field of form.fields) {
switch (field.type) {
case "input": {
// do something for input
break;
}
case "checkbox": {
// do something for checkbox
break;
}
case "options": {
// do something for options
field.options // <-- no error, known to exist
break;
}
default:
((x: never) => console.log("WHAT IS" + x))(field); // guarantee exhaustive
// If an error appears here -------------> ~~~~~
// then you missed a case in the switch statement
}
}
}

好的,希望对您有所帮助。 祝你好运!

链接到代码

如果要限制类型,请使用类型联合:

type formType = string | number | boolean | date;
export interface IForm {
name: string;
url: string;
fields: IField<formType>[] // <-- What is the proper type of this?
}

let form.default = "adsf";     //valid
let form.default = 1;          //valid
let form.default = true;       //valid
let form.default = new date(); //valid
let form.default = null;       //in-valid
let form.default = undefined;  //in-valid
let form.default = never;      //in-valid

相关内容

最新更新