You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular/TypeScript:基于含嵌套结构的Model生成表单类型

适配嵌套对象与数组的Angular表单泛型类型实现

问题背景

最初的基础User接口及表单类型定义如下:

interface User {
  firstName: string;
  lastName: string;
  gender: 'male' | 'female';
}

export type ModelForm<T> = FormGroup<{
  [K in keyof T]: FormControl<T[K]>;
}>

当使用const myForm: ModelForm<User>时类型正常,但当User接口包含嵌套对象与数组结构时:

interface User {
  firstName: string;
  lastName: string;
  gender: 'male' | 'female';
  validity: {
    validFrom: Date,
    validUntil: Date | null,
  }
  addresses: {street: string, houseNumber:number}[],
}

初始的ModelForm泛型无法正确映射嵌套对象(如validity)为FormGroup,也无法将数组(如addresses)映射为FormArray,需要调整泛型逻辑适配复杂结构。

解决方案:递归泛型类型

我们可以通过递归判断类型的方式,让ModelForm自动根据属性类型选择FormControl、FormGroup或FormArray:

import { FormControl, FormGroup, FormArray } from '@angular/forms';

// 辅助类型:判断是否为数组类型
type IsArray<T> = T extends Array<any> ? true : false;
// 辅助类型:判断是否为对象类型(排除基础类型与数组)
type IsObject<T> = T extends object && !IsArray<T> ? true : false;

export type ModelForm<T> = 
  IsArray<T> extends true ? FormArray<ModelForm<T[number]>> :
  IsObject<T> extends true ? FormGroup<{ [K in keyof T]: ModelForm<T[K]> }> :
  FormControl<T>;

类型逻辑说明

  • 若T是数组类型:使用FormArray,数组元素的类型为ModelForm<T[number]>(递归处理数组中的每个对象)
  • 若T是对象类型(非数组):使用FormGroup,每个属性递归应用ModelForm类型
  • 其他基础类型(string、number、Date等):直接使用FormControl<T>

使用示例

定义表单时直接沿用原写法即可,类型会自动适配嵌套结构:

// 类型会自动推导为包含FormGroup(validity)和FormArray(addresses)的FormGroup
const myForm: ModelForm<User> = new FormGroup({
  firstName: new FormControl(''),
  lastName: new FormControl(''),
  gender: new FormControl<'male' | 'female'>('male'),
  validity: new FormGroup({
    validFrom: new FormControl(new Date()),
    validUntil: new FormControl<Date | null>(null),
  }),
  addresses: new FormArray([
    new FormGroup({
      street: new FormControl(''),
      houseNumber: new FormControl(0),
    })
  ])
});

配合FormBuilder使用

实际开发中常用FormBuilder创建表单,类型依然能正确推导:

import { FormBuilder } from '@angular/forms';

constructor(private fb: FormBuilder) {
  const myForm = this.fb.group({
    firstName: '',
    lastName: '',
    gender: 'male',
    validity: this.fb.group({
      validFrom: new Date(),
      validUntil: null,
    }),
    addresses: this.fb.array([
      this.fb.group({
        street: '',
        houseNumber: 0,
      })
    ])
  }) as ModelForm<User>;
}

内容的提问来源于stack exchange,提问作者Rose Nettoyeur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 13:35:58